up su Gitea
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Modules.DesktopWidgets
|
||||
import qs.Services.Media
|
||||
|
||||
DraggableDesktopWidget {
|
||||
id: root
|
||||
|
||||
property var pluginApi: null
|
||||
|
||||
implicitWidth: Math.round(300 * widgetScale)
|
||||
implicitHeight: Math.round(300 * widgetScale)
|
||||
|
||||
showBackground: false
|
||||
|
||||
// Scaled dimensions
|
||||
readonly property int scaledRadiusL: Math.round(Style.radiusL * widgetScale)
|
||||
|
||||
// Settings from plugin
|
||||
readonly property real sensitivity: widgetData?.sensitivity ?? pluginApi?.pluginSettings?.sensitivity ?? pluginApi?.manifest?.metadata?.defaultSettings?.sensitivity
|
||||
readonly property real rotationSpeed: widgetData?.rotationSpeed ?? pluginApi?.pluginSettings?.rotationSpeed ?? pluginApi?.manifest?.metadata?.defaultSettings?.rotationSpeed
|
||||
readonly property real barWidth: widgetData?.barWidth ?? pluginApi?.pluginSettings?.barWidth ?? pluginApi?.manifest?.metadata?.defaultSettings?.barWidth
|
||||
readonly property real ringOpacity: widgetData?.ringOpacity ?? pluginApi?.pluginSettings?.ringOpacity ?? pluginApi?.manifest?.metadata?.defaultSettings?.ringOpacity
|
||||
readonly property real bloomIntensity: widgetData?.bloomIntensity ?? pluginApi.pluginSettings?.bloomIntensity ?? pluginApi?.manifest?.metadata?.defaultSettings?.bloomIntensity
|
||||
readonly property int visualizationMode: widgetData?.visualizationMode ?? pluginApi?.pluginSettings?.visualizationMode ?? pluginApi?.manifest?.metadata?.defaultSettings?.visualizationMode ?? 3
|
||||
readonly property real waveThickness: widgetData?.waveThickness ?? pluginApi?.pluginSettings?.waveThickness ?? pluginApi?.manifest?.metadata?.defaultSettings?.waveThickness ?? 1.0
|
||||
readonly property real innerDiameter: widgetData?.innerDiameter ?? pluginApi?.pluginSettings?.innerDiameter ?? pluginApi?.manifest?.metadata?.defaultSettings?.innerDiameter ?? 0.7
|
||||
readonly property bool fadeWhenIdle: widgetData?.fadeWhenIdle ?? pluginApi?.pluginSettings?.fadeWhenIdle ?? false
|
||||
readonly property bool useCustomColors: widgetData?.useCustomColors ?? pluginApi?.pluginSettings?.useCustomColors ?? false
|
||||
readonly property color customPrimaryColor: widgetData?.customPrimaryColor ?? pluginApi?.pluginSettings?.customPrimaryColor ?? "#6750A4"
|
||||
readonly property color customSecondaryColor: widgetData?.customSecondaryColor ?? pluginApi?.pluginSettings?.customSecondaryColor ?? "#625B71"
|
||||
|
||||
// Animation time for shader (0 to 3600, 1 hour cycle)
|
||||
property real shaderTime: 0
|
||||
NumberAnimation on shaderTime {
|
||||
loops: Animation.Infinite
|
||||
from: 0
|
||||
to: 3600
|
||||
duration: 3600000
|
||||
running: !SpectrumService.isIdle
|
||||
}
|
||||
|
||||
// Hidden canvas that encodes audio data as a 32x1 texture
|
||||
Canvas {
|
||||
id: audioCanvas
|
||||
width: 32
|
||||
height: 1
|
||||
visible: false
|
||||
|
||||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
var values = SpectrumService.values;
|
||||
if (!values || values.length === 0) {
|
||||
ctx.fillStyle = "black";
|
||||
ctx.fillRect(0, 0, 32, 1);
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < 32; i++) {
|
||||
var v = values[i] || 0;
|
||||
// Encode amplitude in grayscale (R=G=B=amplitude)
|
||||
var c = Math.floor(v * 255);
|
||||
ctx.fillStyle = "rgb(" + c + "," + c + "," + c + ")";
|
||||
ctx.fillRect(i, 0, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger canvas repaint when audio data changes
|
||||
Connections {
|
||||
target: SpectrumService
|
||||
function onValuesChanged() {
|
||||
if (!SpectrumService.isIdle) {
|
||||
audioCanvas.requestPaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unique instance ID for SpectrumService registration
|
||||
// This prevents the old widget's destruction from unregistering the new widget
|
||||
readonly property string spectrumInstanceId: "plugin:fancy-audiovisualizer:" + Date.now() + Math.random()
|
||||
|
||||
// Register with SpectrumService when pluginApi becomes available
|
||||
onPluginApiChanged: {
|
||||
if (pluginApi) {
|
||||
SpectrumService.registerComponent(spectrumInstanceId);
|
||||
audioCanvas.requestPaint();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
SpectrumService.unregisterComponent(spectrumInstanceId);
|
||||
}
|
||||
|
||||
// Audio texture source (outside ShaderEffect to avoid 'source' property warning)
|
||||
ShaderEffectSource {
|
||||
id: audioTextureSource
|
||||
sourceItem: audioCanvas
|
||||
live: true
|
||||
hideSource: true
|
||||
}
|
||||
|
||||
// The shader effect visualization
|
||||
ShaderEffect {
|
||||
id: visualizer
|
||||
anchors.fill: parent
|
||||
visible: pluginApi !== null
|
||||
opacity: (root.fadeWhenIdle && SpectrumService.isIdle) ? 0 : 1
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 500; easing.type: Easing.InOutQuad }
|
||||
}
|
||||
|
||||
// Audio texture - named 'source' to match ShaderEffectSource's property and avoid warning
|
||||
property var source: audioTextureSource
|
||||
|
||||
// Uniforms passed to shader
|
||||
property real time: root.shaderTime
|
||||
property real itemWidth: visualizer.width
|
||||
property real itemHeight: visualizer.height
|
||||
property color primaryColor: root.useCustomColors ? root.customPrimaryColor : Color.mPrimary
|
||||
property color secondaryColor: root.useCustomColors ? root.customSecondaryColor : Color.mSecondary
|
||||
property real sensitivity: root.sensitivity
|
||||
property real rotationSpeed: root.rotationSpeed
|
||||
property real barWidth: root.barWidth
|
||||
property real ringOpacity: root.ringOpacity
|
||||
property real cornerRadius: scaledRadiusL
|
||||
property real bloomIntensity: root.bloomIntensity
|
||||
property real visualizationMode: root.visualizationMode
|
||||
property real waveThickness: root.waveThickness
|
||||
property real innerDiameter: root.innerDiameter
|
||||
|
||||
fragmentShader: pluginApi ? Qt.resolvedUrl(pluginApi.pluginDir + "/shaders/visualizer.frag.qsb") : ""
|
||||
}
|
||||
|
||||
// Fallback when shader not loaded
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Color.mSurface
|
||||
radius: scaledRadiusL
|
||||
visible: !visualizer.visible || visualizer.fragmentShader === ""
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "Loading..."
|
||||
color: Color.mOnSurface
|
||||
font.pointSize: Math.round(Style.fontSizeM * widgetScale)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
import qs.Widgets
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
property var pluginApi: null
|
||||
property var widgetSettings: null
|
||||
property var screen: null
|
||||
|
||||
spacing: Style.marginM
|
||||
|
||||
// Local state for editing
|
||||
property real valueSensitivity: widgetSettings?.data?.sensitivity ?? pluginApi?.pluginSettings?.sensitivity ?? 1.5
|
||||
property real valueRotationSpeed: widgetSettings?.data?.rotationSpeed ?? pluginApi?.pluginSettings?.rotationSpeed ?? 0.5
|
||||
property real valueBarWidth: widgetSettings?.data?.barWidth ?? pluginApi?.pluginSettings?.barWidth ?? 0.6
|
||||
property real valueRingOpacity: widgetSettings?.data?.ringOpacity ?? pluginApi?.pluginSettings?.ringOpacity ?? 0.8
|
||||
property real valueBloomIntensity: widgetSettings?.data?.bloomIntensity ?? pluginApi?.pluginSettings?.bloomIntensity ?? 0.5
|
||||
property int valueVisualizationMode: widgetSettings?.data?.visualizationMode ?? pluginApi?.pluginSettings?.visualizationMode ?? 3
|
||||
property real valueWaveThickness: widgetSettings?.data?.waveThickness ?? pluginApi?.pluginSettings?.waveThickness ?? 1.0
|
||||
property real valueInnerDiameter: widgetSettings?.data?.innerDiameter ?? pluginApi?.pluginSettings?.innerDiameter ?? 0.7
|
||||
property bool valueFadeWhenIdle: widgetSettings?.data?.fadeWhenIdle ?? pluginApi?.pluginSettings?.fadeWhenIdle ?? true
|
||||
property bool valueUseCustomColors: widgetSettings?.data?.useCustomColors ?? pluginApi?.pluginSettings?.useCustomColors ?? false
|
||||
property color valueCustomPrimaryColor: widgetSettings?.data?.customPrimaryColor ?? pluginApi?.pluginSettings?.customPrimaryColor ?? "#6750A4"
|
||||
property color valueCustomSecondaryColor: widgetSettings?.data?.customSecondaryColor ?? pluginApi?.pluginSettings?.customSecondaryColor ?? "#625B71"
|
||||
|
||||
// Mode helpers
|
||||
readonly property bool modeHasBars: valueVisualizationMode === 0 || valueVisualizationMode === 3 || valueVisualizationMode === 5
|
||||
readonly property bool modeHasWave: valueVisualizationMode === 1 || valueVisualizationMode === 4 || valueVisualizationMode === 5
|
||||
readonly property bool modeHasRings: valueVisualizationMode >= 2
|
||||
|
||||
NHeader {
|
||||
label: pluginApi?.tr("settings.title") ?? "Visualizer Settings"
|
||||
description: pluginApi?.tr("settings.description") ?? "Configure the audio visualizer appearance"
|
||||
}
|
||||
|
||||
// Visualization mode selector
|
||||
NComboBox {
|
||||
Layout.fillWidth: true
|
||||
label: pluginApi?.tr("settings.visualizationMode") ?? "Visualization Mode"
|
||||
description: pluginApi?.tr("settings.visualizationMode-description") ?? "Choose visualization style"
|
||||
model: [
|
||||
{"key": "0", "name": pluginApi?.tr("settings.mode.bars") ?? "Bars"},
|
||||
{"key": "1", "name": pluginApi?.tr("settings.mode.wave") ?? "Wave"},
|
||||
{"key": "2", "name": pluginApi?.tr("settings.mode.rings") ?? "Rings"},
|
||||
{"key": "3", "name": pluginApi?.tr("settings.mode.barsRings") ?? "Bars + Rings"},
|
||||
{"key": "4", "name": pluginApi?.tr("settings.mode.waveRings") ?? "Wave + Rings"},
|
||||
{"key": "5", "name": pluginApi?.tr("settings.mode.all") ?? "All"}
|
||||
]
|
||||
currentKey: String(root.valueVisualizationMode)
|
||||
onSelected: key => {
|
||||
root.valueVisualizationMode = parseInt(key);
|
||||
root.saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// Wave thickness slider (shown when mode includes wave)
|
||||
NValueSlider {
|
||||
Layout.fillWidth: true
|
||||
visible: root.modeHasWave
|
||||
label: pluginApi?.tr("settings.waveThickness") ?? "Wave Thickness"
|
||||
value: root.valueWaveThickness
|
||||
from: 0.3
|
||||
to: 2.0
|
||||
stepSize: 0.1
|
||||
onMoved: value => {
|
||||
root.valueWaveThickness = value;
|
||||
root.saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// Sensitivity slider
|
||||
NValueSlider {
|
||||
Layout.fillWidth: true
|
||||
label: pluginApi?.tr("settings.sensitivity") ?? "Sensitivity"
|
||||
value: root.valueSensitivity
|
||||
from: 0.5
|
||||
to: 3.0
|
||||
stepSize: 0.1
|
||||
onMoved: value => {
|
||||
root.valueSensitivity = value;
|
||||
root.saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// Rotation speed slider
|
||||
NValueSlider {
|
||||
Layout.fillWidth: true
|
||||
label: pluginApi?.tr("settings.rotationSpeed") ?? "Rotation Speed"
|
||||
value: root.valueRotationSpeed
|
||||
from: 0.0
|
||||
to: 2.0
|
||||
stepSize: 0.1
|
||||
onMoved: value => {
|
||||
root.valueRotationSpeed = value;
|
||||
root.saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// Bar width slider (shown when mode includes bars)
|
||||
NValueSlider {
|
||||
Layout.fillWidth: true
|
||||
visible: root.modeHasBars
|
||||
label: pluginApi?.tr("settings.barWidth") ?? "Bar Width"
|
||||
value: root.valueBarWidth
|
||||
from: 0.2
|
||||
to: 1.0
|
||||
stepSize: 0.1
|
||||
onMoved: value => {
|
||||
root.valueBarWidth = value;
|
||||
root.saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// Ring opacity slider (shown when mode includes rings)
|
||||
NValueSlider {
|
||||
Layout.fillWidth: true
|
||||
visible: root.modeHasRings
|
||||
label: pluginApi?.tr("settings.ringOpacity") ?? "Ring Opacity"
|
||||
value: root.valueRingOpacity
|
||||
from: 0.0
|
||||
to: 1.0
|
||||
stepSize: 0.1
|
||||
onMoved: value => {
|
||||
root.valueRingOpacity = value;
|
||||
root.saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// Base diameter slider
|
||||
NValueSlider {
|
||||
Layout.fillWidth: true
|
||||
label: pluginApi?.tr("settings.innerDiameter") ?? "Inner Diameter"
|
||||
value: root.valueInnerDiameter
|
||||
from: 0
|
||||
to: 1
|
||||
stepSize: 0.05
|
||||
onMoved: value => {
|
||||
root.valueInnerDiameter = value;
|
||||
root.saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// Bloom intensity slider
|
||||
NValueSlider {
|
||||
Layout.fillWidth: true
|
||||
label: pluginApi?.tr("settings.bloomIntensity") ?? "Bloom Intensity"
|
||||
value: root.valueBloomIntensity
|
||||
from: 0.0
|
||||
to: 1.0
|
||||
stepSize: 0.05
|
||||
onMoved: value => {
|
||||
root.valueBloomIntensity = value;
|
||||
root.saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// Fade when idle toggle
|
||||
NToggle {
|
||||
label: pluginApi?.tr("settings.fadeWhenIdle") ?? "Fade When Idle"
|
||||
description: pluginApi?.tr("settings.fadeWhenIdle-description") ?? "Fade out visualizer when no audio is playing"
|
||||
checked: root.valueFadeWhenIdle
|
||||
onToggled: checked => {
|
||||
root.valueFadeWhenIdle = checked;
|
||||
root.saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// Use custom colors toggle
|
||||
NToggle {
|
||||
label: pluginApi?.tr("settings.useCustomColors") ?? "Use Custom Colors"
|
||||
description: pluginApi?.tr("settings.useCustomColors-description") ?? "Override theme colors with custom colors"
|
||||
checked: root.valueUseCustomColors
|
||||
onToggled: checked => {
|
||||
root.valueUseCustomColors = checked;
|
||||
root.saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// Custom primary color picker
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
visible: root.valueUseCustomColors
|
||||
spacing: Style.marginM
|
||||
|
||||
NText {
|
||||
text: pluginApi?.tr("settings.customPrimaryColor") ?? "Primary Color"
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NColorPicker {
|
||||
screen: Screen
|
||||
selectedColor: root.valueCustomPrimaryColor
|
||||
onColorSelected: color => {
|
||||
root.valueCustomPrimaryColor = color;
|
||||
root.saveSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Custom secondary color picker
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
visible: root.valueUseCustomColors
|
||||
spacing: Style.marginM
|
||||
|
||||
NText {
|
||||
text: pluginApi?.tr("settings.customSecondaryColor") ?? "Secondary Color"
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NColorPicker {
|
||||
screen: Screen
|
||||
selectedColor: root.valueCustomSecondaryColor
|
||||
onColorSelected: color => {
|
||||
root.valueCustomSecondaryColor = color;
|
||||
root.saveSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called when user clicks Apply/Save
|
||||
function saveSettings() {
|
||||
if (!widgetSettings)
|
||||
return;
|
||||
|
||||
widgetSettings.data.sensitivity = root.valueSensitivity;
|
||||
widgetSettings.data.rotationSpeed = root.valueRotationSpeed;
|
||||
widgetSettings.data.barWidth = root.valueBarWidth;
|
||||
widgetSettings.data.ringOpacity = root.valueRingOpacity;
|
||||
widgetSettings.data.bloomIntensity = root.valueBloomIntensity;
|
||||
widgetSettings.data.visualizationMode = root.valueVisualizationMode;
|
||||
widgetSettings.data.waveThickness = root.valueWaveThickness;
|
||||
widgetSettings.data.innerDiameter = root.valueInnerDiameter;
|
||||
widgetSettings.data.fadeWhenIdle = root.valueFadeWhenIdle;
|
||||
widgetSettings.data.useCustomColors = root.valueUseCustomColors;
|
||||
widgetSettings.data.customPrimaryColor = root.valueCustomPrimaryColor.toString();
|
||||
widgetSettings.data.customSecondaryColor = root.valueCustomSecondaryColor.toString();
|
||||
|
||||
widgetSettings.save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
# Fancy Audiovisualizer
|
||||
|
||||
A circular audio visualizer desktop widget for Noctalia Shell with shader-based rendering and multiple visualization modes.
|
||||
|
||||
## Features
|
||||
|
||||
- **Multiple Visualization Modes**: Bars, Wave, Rings, or combinations (Bars+Rings, Wave+Rings, All)
|
||||
- **Shader-Based Rendering**: Smooth, GPU-accelerated visualization using custom fragment shaders
|
||||
- **Theme Integration**: Automatically uses Noctalia theme colors, with optional custom color override
|
||||
- **Configurable Appearance**: Adjust sensitivity, rotation speed, bar width, ring opacity, bloom intensity, and more
|
||||
- **Idle Fade**: Optional fade-out when no audio is playing
|
||||
|
||||
## Installation
|
||||
|
||||
This plugin is part of the `noctalia-plugins` repository.
|
||||
|
||||
## Configuration
|
||||
|
||||
Access the plugin settings in Noctalia to configure the following options:
|
||||
|
||||
- **Visualization Mode**: Choose between Bars, Wave, Rings, Bars+Rings, Wave+Rings, or All
|
||||
- **Wave Thickness**: Thickness of the wave visualization (when enabled)
|
||||
- **Sensitivity**: Audio response sensitivity (0.5 - 3.0)
|
||||
- **Rotation Speed**: Speed of visualization rotation (0.0 - 2.0)
|
||||
- **Bar Width**: Width of audio bars (when enabled)
|
||||
- **Ring Opacity**: Opacity of background rings (when enabled)
|
||||
- **Inner Diameter**: Size of the inner empty area
|
||||
- **Bloom Intensity**: Glow/bloom effect strength
|
||||
- **Fade When Idle**: Fade out when no audio is playing
|
||||
- **Custom Colors**: Override theme colors with custom primary and secondary colors
|
||||
|
||||
## Usage
|
||||
|
||||
Add the widget to your desktop via the Noctalia desktop widgets interface. The visualizer responds to system audio.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Noctalia 3.7.2 or later
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"settings": {
|
||||
"barWidth": "Balkenbreite",
|
||||
"bloomIntensity": "Blütenintensität",
|
||||
"customPrimaryColor": "Primärfarbe",
|
||||
"customSecondaryColor": "Sekundärfarbe",
|
||||
"description": "Audiovisualisierungsdarstellung konfigurieren",
|
||||
"fadeWhenIdle": "Ausblenden bei Inaktivität",
|
||||
"fadeWhenIdle-description": "Visualisierer ausblenden, wenn keine Audioausgabe erfolgt",
|
||||
"innerDiameter": "Innendurchmesser",
|
||||
"mode": {
|
||||
"all": "Alle",
|
||||
"bars": "Bars",
|
||||
"barsRings": "Barren + Ringe",
|
||||
"rings": "Ringe",
|
||||
"wave": "Welle",
|
||||
"waveRings": "Welle + Ringe"
|
||||
},
|
||||
"ringOpacity": "Ring-Opazität",
|
||||
"rotationSpeed": "Drehzahl",
|
||||
"sensitivity": "Empfindlichkeit",
|
||||
"title": "Visualisierungseinstellungen",
|
||||
"useCustomColors": "Benutzerdefinierte Farben verwenden",
|
||||
"useCustomColors-description": "Themenfarben mit benutzerdefinierten Farben überschreiben",
|
||||
"visualizationMode": "Visualisierungsmodus",
|
||||
"visualizationMode-description": "Visualisierungsstil wählen",
|
||||
"waveThickness": "Wellendicke"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"settings": {
|
||||
"barWidth": "Bar Width",
|
||||
"bloomIntensity": "Bloom Intensity",
|
||||
"customPrimaryColor": "Primary Color",
|
||||
"customSecondaryColor": "Secondary Color",
|
||||
"description": "Configure the audio visualizer appearance",
|
||||
"fadeWhenIdle": "Fade When Idle",
|
||||
"fadeWhenIdle-description": "Fade out visualizer when no audio is playing",
|
||||
"innerDiameter": "Inner Diameter",
|
||||
"mode": {
|
||||
"all": "All",
|
||||
"bars": "Bars",
|
||||
"barsRings": "Bars + Rings",
|
||||
"rings": "Rings",
|
||||
"wave": "Wave",
|
||||
"waveRings": "Wave + Rings"
|
||||
},
|
||||
"ringOpacity": "Ring Opacity",
|
||||
"rotationSpeed": "Rotation Speed",
|
||||
"sensitivity": "Sensitivity",
|
||||
"title": "Visualizer Settings",
|
||||
"useCustomColors": "Use Custom Colors",
|
||||
"useCustomColors-description": "Override theme colors with custom colors",
|
||||
"visualizationMode": "Visualization Mode",
|
||||
"visualizationMode-description": "Choose visualization style",
|
||||
"waveThickness": "Wave Thickness"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"settings": {
|
||||
"barWidth": "Ancho de barra",
|
||||
"bloomIntensity": "Intensidad de floración",
|
||||
"customPrimaryColor": "Color primario",
|
||||
"customSecondaryColor": "Color secundario",
|
||||
"description": "Configurar la apariencia del visualizador de audio",
|
||||
"fadeWhenIdle": "Desvanecer al estar inactivo",
|
||||
"fadeWhenIdle-description": "Atenuar el visualizador cuando no se reproduce audio.",
|
||||
"innerDiameter": "Diámetro interior",
|
||||
"mode": {
|
||||
"all": "Todo",
|
||||
"bars": "Barras",
|
||||
"barsRings": "Barras + Anillas",
|
||||
"rings": "Anillos",
|
||||
"wave": "Ola",
|
||||
"waveRings": "Onda + Anillos"
|
||||
},
|
||||
"ringOpacity": "Opacidad del anillo",
|
||||
"rotationSpeed": "Velocidad de rotación",
|
||||
"sensitivity": "Sensibilidad",
|
||||
"title": "Ajustes del Visualizador",
|
||||
"useCustomColors": "Usar colores personalizados",
|
||||
"useCustomColors-description": "Anular los colores del tema con colores personalizados",
|
||||
"visualizationMode": "Modo de visualización",
|
||||
"visualizationMode-description": "Elegir estilo de visualización",
|
||||
"waveThickness": "Espesor de la onda"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"settings": {
|
||||
"barWidth": "Largeur de la barre",
|
||||
"bloomIntensity": "Intensité de l'éclat",
|
||||
"customPrimaryColor": "Couleur primaire",
|
||||
"customSecondaryColor": "Couleur secondaire",
|
||||
"description": "Configurer l'apparence du visualiseur audio",
|
||||
"fadeWhenIdle": "Estomper en cas d'inactivité",
|
||||
"fadeWhenIdle-description": "Estomper le visualiseur en l'absence de son.",
|
||||
"innerDiameter": "Diamètre intérieur",
|
||||
"mode": {
|
||||
"all": "Tout",
|
||||
"bars": "Barres",
|
||||
"barsRings": "Barres + Anneaux",
|
||||
"rings": "Anneaux",
|
||||
"wave": "Vague",
|
||||
"waveRings": "Onde + Anneaux"
|
||||
},
|
||||
"ringOpacity": "Opacité de l'anneau",
|
||||
"rotationSpeed": "Vitesse de rotation",
|
||||
"sensitivity": "Sensibilité",
|
||||
"title": "Paramètres du visualiseur",
|
||||
"useCustomColors": "Utiliser des couleurs personnalisées",
|
||||
"useCustomColors-description": "Remplacer les couleurs du thème par des couleurs personnalisées",
|
||||
"visualizationMode": "Mode de visualisation",
|
||||
"visualizationMode-description": "Choisir le style de visualisation",
|
||||
"waveThickness": "Épaisseur de la vague"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"settings": {
|
||||
"barWidth": "Sávszélesség",
|
||||
"bloomIntensity": "Virágzás intenzitása",
|
||||
"customPrimaryColor": "Elsődleges szín",
|
||||
"customSecondaryColor": "Másodlagos szín",
|
||||
"description": "Az audio vizualizáló megjelenésének beállítása",
|
||||
"fadeWhenIdle": "Halványítás tétlenség esetén",
|
||||
"fadeWhenIdle-description": "A vizualizáció elhalványítása, ha nincs hang lejátszva",
|
||||
"innerDiameter": "Belső átmérő",
|
||||
"mode": {
|
||||
"all": "Összes",
|
||||
"bars": "Sávok",
|
||||
"barsRings": "Sávok + Gyűrűk",
|
||||
"rings": "Gyűrűk",
|
||||
"wave": "Hullám",
|
||||
"waveRings": "Hullám + Gyűrűk"
|
||||
},
|
||||
"ringOpacity": "Gyűrű átlátszósága",
|
||||
"rotationSpeed": "Forgási sebesség",
|
||||
"sensitivity": "Érzékenység",
|
||||
"title": "Vizualizációs beállítások",
|
||||
"useCustomColors": "Egyéni színek használata",
|
||||
"useCustomColors-description": "A téma színeinek felülírása egyéni színekkel",
|
||||
"visualizationMode": "Megjelenítési mód",
|
||||
"visualizationMode-description": "Válassz megjelenítési stílust",
|
||||
"waveThickness": "Hullámvastagság"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"settings": {
|
||||
"barWidth": "Breite der Leiste",
|
||||
"bloomIntensity": "Intensità Bloom",
|
||||
"customPrimaryColor": "Ngjyra parësore",
|
||||
"customSecondaryColor": "Ngjyra dytësore",
|
||||
"description": "Konfigurieren Sie das Aussehen des Audio-Visualisierers.",
|
||||
"fadeWhenIdle": "Verblassen im Leerlauf",
|
||||
"fadeWhenIdle-description": "Vizualizátor eltüntetése, ha nincs hang lejátszva.",
|
||||
"innerDiameter": "Innendurchmesser",
|
||||
"mode": {
|
||||
"all": "Kaikki",
|
||||
"bars": "Pritličja",
|
||||
"barsRings": "Stangen + Ringe",
|
||||
"rings": "Hringar",
|
||||
"wave": "Val",
|
||||
"waveRings": "Welle + Ringe"
|
||||
},
|
||||
"ringOpacity": "Neprůhlednost prstence",
|
||||
"rotationSpeed": "Schnelllaufdrehzahl",
|
||||
"sensitivity": "Ndjeshmëri",
|
||||
"title": "Mga Setting ng Visualizer",
|
||||
"useCustomColors": "Përdorni Ngjyra të Personalizuara",
|
||||
"useCustomColors-description": "Ngjyrat e personalizuara mbivendosin ngjyrat e temës",
|
||||
"visualizationMode": "Módszer megjelenítése",
|
||||
"visualizationMode-description": "Zgjidhni stilin e vizualizimit",
|
||||
"waveThickness": "Onda-lodiera"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"settings": {
|
||||
"barWidth": "バーの幅",
|
||||
"bloomIntensity": "開花強度",
|
||||
"customPrimaryColor": "原色",
|
||||
"customSecondaryColor": "二次色",
|
||||
"description": "オーディオビジュアライザーの外観を設定します。",
|
||||
"fadeWhenIdle": "アイドル時にフェード",
|
||||
"fadeWhenIdle-description": "音声が再生されていないときは、ビジュアライザーをフェードアウトする",
|
||||
"innerDiameter": "内径",
|
||||
"mode": {
|
||||
"all": "すべて",
|
||||
"bars": "バー",
|
||||
"barsRings": "鉄棒と輪",
|
||||
"rings": "リング",
|
||||
"wave": "波",
|
||||
"waveRings": "波紋 + リング"
|
||||
},
|
||||
"ringOpacity": "リングの不透明度",
|
||||
"rotationSpeed": "回転速度",
|
||||
"sensitivity": "感度",
|
||||
"title": "ビジュアライザー設定",
|
||||
"useCustomColors": "カスタムカラーを使用する",
|
||||
"useCustomColors-description": "カスタムカラーでテーマの色をオーバーライドする",
|
||||
"visualizationMode": "可視化モード",
|
||||
"visualizationMode-description": "視覚化のスタイルを選択",
|
||||
"waveThickness": "波厚"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"settings": {
|
||||
"barWidth": "Firehiya Barê",
|
||||
"bloomIntensity": "Zehfîya Gurtî",
|
||||
"customPrimaryColor": "Rengê Seretayî",
|
||||
"customSecondaryColor": "Rengê Duyemîn",
|
||||
"description": "Xuyakirina dîmenê bihîstwerî yê dengî",
|
||||
"fadeWhenIdle": "Dema de bêdengiyê vemirîne",
|
||||
"fadeWhenIdle-description": "Dîmenkerê dema ku tu deng lê nabe, hêdî hêdî winda bike",
|
||||
"innerDiameter": "Dirêjbûna Hundir",
|
||||
"mode": {
|
||||
"all": "Hemû",
|
||||
"bars": "Bars",
|
||||
"barsRings": "Bars + Rings",
|
||||
"rings": "Xelqet",
|
||||
"wave": "Pêl",
|
||||
"waveRings": "Pêl + Xelek"
|
||||
},
|
||||
"ringOpacity": "Ronahiya Zengil",
|
||||
"rotationSpeed": "Leza Şewitandinê",
|
||||
"sensitivity": "Hesasiyet",
|
||||
"title": "Mîhengên Dîmenderê",
|
||||
"useCustomColors": "Rengên Xweser Bikar Bîne",
|
||||
"useCustomColors-description": "Rengên mijarê bi rengên xwerû biguherîne",
|
||||
"visualizationMode": "Şêwaza Dîmenkirinê",
|
||||
"visualizationMode-description": "Şêwaza dîtinê hilbijêre",
|
||||
"waveThickness": "Qalinîya Pêlê"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"settings": {
|
||||
"barWidth": "Balkbreedte",
|
||||
"bloomIntensity": "Bloei Intensiteit",
|
||||
"customPrimaryColor": "Primaire kleur",
|
||||
"customSecondaryColor": "Secundaire kleur",
|
||||
"description": "Configureer de weergave van de audio visualisatie.",
|
||||
"fadeWhenIdle": "Vervagen bij inactiviteit",
|
||||
"fadeWhenIdle-description": "Visualiseerder laten vervagen wanneer er geen audio wordt afgespeeld.",
|
||||
"innerDiameter": "Binnendiameter",
|
||||
"mode": {
|
||||
"all": "Alles",
|
||||
"bars": "Bars",
|
||||
"barsRings": "Rekstok + Ringen",
|
||||
"rings": "Ringen",
|
||||
"wave": "Golf",
|
||||
"waveRings": "Golf + Ringen"
|
||||
},
|
||||
"ringOpacity": "Ringondoorzichtigheid",
|
||||
"rotationSpeed": "Rotatiesnelheid",
|
||||
"sensitivity": "Gevoeligheid",
|
||||
"title": "Visualisatie-instellingen",
|
||||
"useCustomColors": "Aangepaste kleuren gebruiken",
|
||||
"useCustomColors-description": "Themakleuren overschrijven met aangepaste kleuren",
|
||||
"visualizationMode": "Visualisatiemodus",
|
||||
"visualizationMode-description": "Kies visualisatiestijl",
|
||||
"waveThickness": "Golflaagdikte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"settings": {
|
||||
"barWidth": "Szerokość paska",
|
||||
"bloomIntensity": "Intensywność Rozkwitu",
|
||||
"customPrimaryColor": "Kolor podstawowy",
|
||||
"customSecondaryColor": "Kolor dodatkowy",
|
||||
"description": "Skonfiguruj wygląd wizualizatora dźwięku",
|
||||
"fadeWhenIdle": "Przyciemnij przy bezczynności",
|
||||
"fadeWhenIdle-description": "Wycisz wizualizator, gdy nie jest odtwarzany dźwięk",
|
||||
"innerDiameter": "Średnica wewnętrzna",
|
||||
"mode": {
|
||||
"all": "Wszystkie",
|
||||
"bars": "Paski",
|
||||
"barsRings": "Paski + Pierścienie",
|
||||
"rings": "Pierścienie",
|
||||
"wave": "Fala",
|
||||
"waveRings": "Fala + Pierścienie"
|
||||
},
|
||||
"ringOpacity": "Krycie pierścienia",
|
||||
"rotationSpeed": "Prędkość obrotu",
|
||||
"sensitivity": "Czułość",
|
||||
"title": "Ustawienia wizualizatora",
|
||||
"useCustomColors": "Użyj własnych kolorów",
|
||||
"useCustomColors-description": "Zastąp kolory motywu własnymi kolorami",
|
||||
"visualizationMode": "Tryb wizualizacji",
|
||||
"visualizationMode-description": "Wybierz styl wizualizacji",
|
||||
"waveThickness": "Grubość fali"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"settings": {
|
||||
"barWidth": "Largura da Barra",
|
||||
"bloomIntensity": "Intensidade do Bloom",
|
||||
"customPrimaryColor": "Cor primária",
|
||||
"customSecondaryColor": "Cor secundária",
|
||||
"description": "Configurar a aparência do visualizador de áudio",
|
||||
"fadeWhenIdle": "Desaparecer quando inativo",
|
||||
"fadeWhenIdle-description": "Desvanecer o visualizador quando nenhum áudio estiver sendo reproduzido.",
|
||||
"innerDiameter": "Diâmetro interno",
|
||||
"mode": {
|
||||
"all": "Tudo",
|
||||
"bars": "Barras",
|
||||
"barsRings": "Barras + Argolas",
|
||||
"rings": "Anéis",
|
||||
"wave": "Onda",
|
||||
"waveRings": "Onda + Anéis"
|
||||
},
|
||||
"ringOpacity": "Opacidade do Anel",
|
||||
"rotationSpeed": "Velocidade de rotação",
|
||||
"sensitivity": "Sensibilidade",
|
||||
"title": "Configurações do Visualizador",
|
||||
"useCustomColors": "Usar Cores Personalizadas",
|
||||
"useCustomColors-description": "Substituir as cores do tema por cores personalizadas",
|
||||
"visualizationMode": "Modo de Visualização",
|
||||
"visualizationMode-description": "Escolha o estilo de visualização",
|
||||
"waveThickness": "Espessura da onda"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"settings": {
|
||||
"barWidth": "Ширина полосы",
|
||||
"bloomIntensity": "Интенсивность свечения",
|
||||
"customPrimaryColor": "Основной цвет",
|
||||
"customSecondaryColor": "Вторичный цвет",
|
||||
"description": "Настроить внешний вид визуализатора звука",
|
||||
"fadeWhenIdle": "Затухать в режиме ожидания",
|
||||
"fadeWhenIdle-description": "Затухать визуализатору при отсутствии воспроизведения аудио.",
|
||||
"innerDiameter": "Внутренний диаметр",
|
||||
"mode": {
|
||||
"all": "Всё",
|
||||
"bars": "Бары",
|
||||
"barsRings": "Турники + Кольца",
|
||||
"rings": "Кольца",
|
||||
"wave": "Волна",
|
||||
"waveRings": "Волна + Кольца"
|
||||
},
|
||||
"ringOpacity": "Прозрачность кольца",
|
||||
"rotationSpeed": "Скорость вращения",
|
||||
"sensitivity": "Чувствительность",
|
||||
"title": "Настройки визуализатора",
|
||||
"useCustomColors": "Использовать пользовательские цвета",
|
||||
"useCustomColors-description": "Переопределить цвета темы пользовательскими цветами",
|
||||
"visualizationMode": "Режим визуализации",
|
||||
"visualizationMode-description": "Выберите стиль визуализации",
|
||||
"waveThickness": "Толщина волны"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"settings": {
|
||||
"barWidth": "Çubuk Genişliği",
|
||||
"bloomIntensity": "Çiçeklenme Yoğunluğu",
|
||||
"customPrimaryColor": "Ana Renk",
|
||||
"customSecondaryColor": "İkincil Renk",
|
||||
"description": "Ses görselleştirici görünümünü yapılandır",
|
||||
"fadeWhenIdle": "Boşta Kalınca Karart",
|
||||
"fadeWhenIdle-description": "Ses çalmadığında görselleştiriciyi soldur.",
|
||||
"innerDiameter": "İç Çap",
|
||||
"mode": {
|
||||
"all": "Tüm",
|
||||
"bars": "Barlar",
|
||||
"barsRings": "Barfiks Demiri + Halkalar",
|
||||
"rings": "Yüzükler",
|
||||
"wave": "Dalga",
|
||||
"waveRings": "Dalga + Halkalar"
|
||||
},
|
||||
"ringOpacity": "Halka Opaklığı",
|
||||
"rotationSpeed": "Dönme Hızı",
|
||||
"sensitivity": "Hassasiyet",
|
||||
"title": "Görselleştirici Ayarları",
|
||||
"useCustomColors": "Özel Renkleri Kullan",
|
||||
"useCustomColors-description": "Tema renklerini özel renklerle geçersiz kıl",
|
||||
"visualizationMode": "Görselleştirme Modu",
|
||||
"visualizationMode-description": "Görselleştirme stilini seçin",
|
||||
"waveThickness": "Dalga Kalınlığı"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"settings": {
|
||||
"barWidth": "Ширина бруска",
|
||||
"bloomIntensity": "Інтенсивність світіння",
|
||||
"customPrimaryColor": "Основний колір",
|
||||
"customSecondaryColor": "Вторинний колір",
|
||||
"description": "Налаштуйте вигляд аудіовізуалізатора",
|
||||
"fadeWhenIdle": "Згасати в режимі очікування",
|
||||
"fadeWhenIdle-description": "Затемнювати візуалізатор, коли не відтворюється звук.",
|
||||
"innerDiameter": "Внутрішній діаметр",
|
||||
"mode": {
|
||||
"all": "Все",
|
||||
"bars": "Бари",
|
||||
"barsRings": "Бруси + Кільця",
|
||||
"rings": "Кільця",
|
||||
"wave": "Хвиля",
|
||||
"waveRings": "Хвиля + Кільця"
|
||||
},
|
||||
"ringOpacity": "Прозорість кільця",
|
||||
"rotationSpeed": "Швидкість обертання",
|
||||
"sensitivity": "Чутливість",
|
||||
"title": "Налаштування візуалізатора",
|
||||
"useCustomColors": "Використовувати власні кольори",
|
||||
"useCustomColors-description": "Перевизначити кольори теми власними кольорами",
|
||||
"visualizationMode": "Режим візуалізації",
|
||||
"visualizationMode-description": "Виберіть стиль візуалізації",
|
||||
"waveThickness": "Товщина хвилі"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"settings": {
|
||||
"barWidth": "Độ rộng thanh",
|
||||
"bloomIntensity": "Cường độ hiệu ứng bloom",
|
||||
"customPrimaryColor": "Màu chính tùy chỉnh",
|
||||
"customSecondaryColor": "Màu phụ tùy chỉnh",
|
||||
"description": "Cấu hình giao diện trình hiển thị âm thanh",
|
||||
"fadeWhenIdle": "Làm mờ khi không hoạt động",
|
||||
"fadeWhenIdle-description": "Làm mờ trình hiển thị khi không có âm thanh",
|
||||
"innerDiameter": "Đường kính bên trong",
|
||||
"mode": {
|
||||
"all": "Tất cả",
|
||||
"bars": "Thanh",
|
||||
"barsRings": "Thanh + Vòng",
|
||||
"rings": "Vòng",
|
||||
"wave": "Sóng",
|
||||
"waveRings": "Sóng + Vòng"
|
||||
},
|
||||
"ringOpacity": "Độ mờ vòng",
|
||||
"rotationSpeed": "Tốc độ xoay",
|
||||
"sensitivity": "Độ nhạy",
|
||||
"title": "Cài đặt trình hiển thị",
|
||||
"useCustomColors": "Dùng màu tùy chỉnh",
|
||||
"useCustomColors-description": "Ghi đè màu giao diện bằng màu tùy chỉnh",
|
||||
"visualizationMode": "Chế độ hiển thị",
|
||||
"visualizationMode-description": "Chọn kiểu hiển thị",
|
||||
"waveThickness": "Độ dày sóng"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"settings": {
|
||||
"barWidth": "条宽",
|
||||
"bloomIntensity": "光晕强度",
|
||||
"customPrimaryColor": "原色",
|
||||
"customSecondaryColor": "间色",
|
||||
"description": "配置音频可视化效果外观",
|
||||
"fadeWhenIdle": "空闲时淡出",
|
||||
"fadeWhenIdle-description": "当没有音频播放时淡出可视化工具",
|
||||
"innerDiameter": "内径",
|
||||
"mode": {
|
||||
"all": "全部",
|
||||
"bars": "酒吧",
|
||||
"barsRings": "单杠 + 吊环",
|
||||
"rings": "戒指",
|
||||
"wave": "波浪",
|
||||
"waveRings": "波浪 + 环"
|
||||
},
|
||||
"ringOpacity": "环透明度",
|
||||
"rotationSpeed": "转速",
|
||||
"sensitivity": "敏感性",
|
||||
"title": "可视化设置",
|
||||
"useCustomColors": "使用自定义颜色",
|
||||
"useCustomColors-description": "使用自定义颜色覆盖主题颜色",
|
||||
"visualizationMode": "可视化模式",
|
||||
"visualizationMode-description": "选择可视化风格",
|
||||
"waveThickness": "波浪厚度"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"id": "fancy-audiovisualizer",
|
||||
"name": "Fancy Audiovisualizer",
|
||||
"version": "1.1.2",
|
||||
"minNoctaliaVersion": "4.6.6",
|
||||
"author": "Noctalia Team",
|
||||
"official": true,
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/noctalia-dev/noctalia-plugins",
|
||||
"description": "Lemmy's fancy circular audio visualizer.",
|
||||
"tags": [
|
||||
"Desktop",
|
||||
"Audio"
|
||||
],
|
||||
"entryPoints": {
|
||||
"desktopWidget": "DesktopWidget.qml",
|
||||
"desktopWidgetSettings": "DesktopWidgetSettings.qml"
|
||||
},
|
||||
"dependencies": {
|
||||
"plugins": []
|
||||
},
|
||||
"metadata": {
|
||||
"defaultSettings": {
|
||||
"sensitivity": 1.5,
|
||||
"rotationSpeed": 0.5,
|
||||
"barWidth": 0.6,
|
||||
"ringOpacity": 0.8,
|
||||
"bloomIntensity": 0.5,
|
||||
"visualizationMode": 3,
|
||||
"waveThickness": 1.0,
|
||||
"innerDiameter": 0.7,
|
||||
"fadeWhenIdle": false,
|
||||
"useCustomColors": false,
|
||||
"customPrimaryColor": "#6750A4",
|
||||
"customSecondaryColor": "#625B71"
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 575 KiB |
@@ -0,0 +1,559 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 qt_TexCoord0;
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
layout(std140, binding = 0) uniform buf {
|
||||
mat4 qt_Matrix;
|
||||
float qt_Opacity;
|
||||
float time;
|
||||
float itemWidth;
|
||||
float itemHeight;
|
||||
vec4 primaryColor;
|
||||
vec4 secondaryColor;
|
||||
float sensitivity;
|
||||
float rotationSpeed;
|
||||
float barWidth;
|
||||
float ringOpacity;
|
||||
float cornerRadius;
|
||||
float bloomIntensity;
|
||||
float visualizationMode; // 0=bars, 1=wave, 2=rings, 3=bars+rings, 4=wave+rings, 5=all
|
||||
float waveThickness;
|
||||
float innerDiameter;
|
||||
} ubuf;
|
||||
|
||||
// Mode helper functions
|
||||
bool hasRings() { return ubuf.visualizationMode >= 2.0; }
|
||||
bool hasBars() { return ubuf.visualizationMode == 0.0 || ubuf.visualizationMode == 3.0 || ubuf.visualizationMode >= 5.0; }
|
||||
bool hasWave() { return ubuf.visualizationMode == 1.0 || ubuf.visualizationMode == 4.0 || ubuf.visualizationMode >= 5.0; }
|
||||
|
||||
layout(binding = 1) uniform sampler2D source;
|
||||
|
||||
#define TWOPI 6.28318530718
|
||||
#define PI 3.14159265359
|
||||
#define NBARS 32
|
||||
|
||||
// Sample audio amplitude at normalized position (0.0-1.0)
|
||||
float getAudio(float pos) {
|
||||
return texture(source, vec2(clamp(pos, 0.0, 1.0), 0.5)).r;
|
||||
}
|
||||
|
||||
// Smoothed audio sampling with interpolation
|
||||
float smoothAudio(float pos) {
|
||||
float idx = pos * float(NBARS - 1);
|
||||
float frac = fract(idx);
|
||||
float i0 = floor(idx) / float(NBARS - 1);
|
||||
float i1 = ceil(idx) / float(NBARS - 1);
|
||||
return mix(getAudio(i0), getAudio(i1), frac) * ubuf.sensitivity;
|
||||
}
|
||||
|
||||
// Frequency band helpers
|
||||
float getBass() { return smoothAudio(0.05); }
|
||||
float getMid() { return smoothAudio(0.3); }
|
||||
float getHighMid() { return smoothAudio(0.6); }
|
||||
float getTreble() { return smoothAudio(0.9); }
|
||||
|
||||
// SDF for rounded rectangle
|
||||
float roundedBoxSDF(vec2 center, vec2 size, float radius) {
|
||||
vec2 q = abs(center) - size + radius;
|
||||
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radius;
|
||||
}
|
||||
|
||||
// Compute polar wave visualization
|
||||
vec4 computePolarWave(vec2 uv, float iTime, float bass, float mid, float highMid, float treble) {
|
||||
float aspect = ubuf.itemWidth / ubuf.itemHeight;
|
||||
vec2 centered = (uv - 0.5) * 2.0;
|
||||
centered.x *= aspect;
|
||||
|
||||
float theta = atan(centered.y, centered.x);
|
||||
float d = length(centered);
|
||||
float innerRadius = ubuf.innerDiameter / 2.0;
|
||||
float baseRadius = 0.35; // Fixed reference for outer extent
|
||||
|
||||
vec4 color = vec4(0.0);
|
||||
|
||||
// RING SYSTEM
|
||||
if (hasRings()) {
|
||||
// Center Waves
|
||||
if (d < innerRadius * 0.6) {
|
||||
float wave = mid * 0.8;
|
||||
float ripple = sin(d * 25.0 + wave * 15.0 - iTime * 2.0);
|
||||
if (ripple > 0.7) {
|
||||
float intensity = clamp(mid * 0.6, 0.0, 0.4);
|
||||
vec4 waveColor = ubuf.secondaryColor * intensity * ubuf.ringOpacity;
|
||||
color = max(color, waveColor);
|
||||
}
|
||||
}
|
||||
|
||||
// Energy Ring
|
||||
float energyRad = innerRadius * 0.65;
|
||||
float energyThickness = 0.015 + clamp(highMid * 0.02, 0.0, 0.03);
|
||||
if (d > energyRad - energyThickness && d < energyRad + energyThickness) {
|
||||
float segmentAngle = theta * 8.0 + highMid * 3.0 + iTime;
|
||||
if (mod(segmentAngle, 1.0) < 0.6) {
|
||||
float alpha = clamp(highMid * 2.0, 0.3, 1.0) * ubuf.ringOpacity;
|
||||
vec4 energyColor = mix(ubuf.primaryColor, ubuf.secondaryColor, 0.5) * alpha;
|
||||
color = max(color, energyColor);
|
||||
}
|
||||
}
|
||||
|
||||
// Particle Ring
|
||||
float particleRad = innerRadius * 0.75;
|
||||
if (d > particleRad - 0.02 && d < particleRad + 0.02) {
|
||||
float particleAngle = theta + treble * 2.0 + iTime * 0.5;
|
||||
float particleSpacing = TWOPI / 16.0;
|
||||
if (mod(particleAngle, particleSpacing) < 0.15) {
|
||||
float brightness = 0.5 + clamp(treble * 1.5, 0.0, 0.5);
|
||||
vec4 particleColor = ubuf.secondaryColor * brightness * ubuf.ringOpacity;
|
||||
color = max(color, particleColor);
|
||||
}
|
||||
}
|
||||
|
||||
// Tech Grid Ring
|
||||
float gridRad = innerRadius * 0.85;
|
||||
if (d > gridRad - 0.012 && d < gridRad + 0.012) {
|
||||
float gridAngle = theta + iTime * ubuf.rotationSpeed;
|
||||
float gridDensity = 0.08 + clamp(mid * 0.05, 0.0, 0.1);
|
||||
if (mod(gridAngle, 0.2) < gridDensity) {
|
||||
vec4 gridColor = ubuf.primaryColor * 0.7 * ubuf.ringOpacity;
|
||||
gridColor.rgb += vec3(0.1, 0.15, 0.2) * clamp(mid, 0.0, 0.8);
|
||||
color = max(color, gridColor);
|
||||
}
|
||||
}
|
||||
|
||||
// Accent Ring
|
||||
float accentRad = innerRadius * 0.92;
|
||||
float pulse = clamp(bass * 0.08, 0.0, 0.05);
|
||||
if (d > accentRad - pulse - 0.008 && d < accentRad + pulse + 0.015) {
|
||||
float colorShift = clamp(bass * 0.5, 0.0, 1.0);
|
||||
vec4 ringColor = mix(ubuf.secondaryColor * 0.7, ubuf.primaryColor, colorShift);
|
||||
ringColor.a = ubuf.ringOpacity;
|
||||
ringColor.rgb *= 1.0 + bass * 0.3;
|
||||
color = max(color, ringColor);
|
||||
}
|
||||
|
||||
// Outer Ring
|
||||
float outerRad = innerRadius + bass * 0.05;
|
||||
if (d > outerRad - 0.008 && d < outerRad + 0.008) {
|
||||
vec4 outerColor = ubuf.primaryColor * ubuf.ringOpacity;
|
||||
outerColor.rgb += vec3(0.2, 0.3, 0.4) * clamp(treble * 0.5, 0.0, 0.3);
|
||||
outerColor.rgb *= 1.0 + bass * 0.4;
|
||||
color = max(color, outerColor);
|
||||
}
|
||||
}
|
||||
|
||||
// POLAR WAVE - filled shape with mirrored bands (64 visual bands from 32 samples)
|
||||
if (hasWave()) {
|
||||
float adjustedTheta = theta + PI + iTime * ubuf.rotationSpeed * 0.2;
|
||||
|
||||
// Map angle to 0-1 range around the full circle
|
||||
float normalizedAngle = mod(adjustedTheta, TWOPI) / TWOPI;
|
||||
|
||||
// Mirror: first half (0-0.5) maps to bands 0->31, second half (0.5-1) maps back 31->0
|
||||
float mirroredPos = normalizedAngle < 0.5 ? normalizedAngle * 2.0 : (1.0 - normalizedAngle) * 2.0;
|
||||
|
||||
// Catmull-Rom spline interpolation for smooth curve through mirrored bands
|
||||
float bandPos = mirroredPos * float(NBARS - 1);
|
||||
float fband1 = floor(bandPos);
|
||||
float fband0 = max(fband1 - 1.0, 0.0);
|
||||
float fband2 = min(fband1 + 1.0, float(NBARS - 1));
|
||||
float fband3 = min(fband1 + 2.0, float(NBARS - 1));
|
||||
|
||||
float t = fract(bandPos);
|
||||
|
||||
// Sample the 4 control points
|
||||
float p0 = getAudio(fband0 / float(NBARS - 1)) * ubuf.sensitivity;
|
||||
float p1 = getAudio(fband1 / float(NBARS - 1)) * ubuf.sensitivity;
|
||||
float p2 = getAudio(fband2 / float(NBARS - 1)) * ubuf.sensitivity;
|
||||
float p3 = getAudio(fband3 / float(NBARS - 1)) * ubuf.sensitivity;
|
||||
|
||||
// Catmull-Rom spline interpolation
|
||||
float t2 = t * t;
|
||||
float t3 = t2 * t;
|
||||
float smoothedAudio = 0.5 * (
|
||||
(2.0 * p1) +
|
||||
(-p0 + p2) * t +
|
||||
(2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2 +
|
||||
(-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3
|
||||
);
|
||||
smoothedAudio = max(smoothedAudio, 0.0);
|
||||
|
||||
// Calculate wave radius at this angle
|
||||
float waveDisplacement = smoothedAudio * 0.5;
|
||||
float waveRadius = baseRadius + waveDisplacement; // Fixed outer extent
|
||||
|
||||
// Fill the entire area from inner to wave edge
|
||||
if (d >= innerRadius && d <= waveRadius) {
|
||||
float fillFactor = (d - innerRadius) / max(waveRadius - innerRadius, 0.001);
|
||||
|
||||
// Gradient from primary at base to accent at edge
|
||||
vec3 fillColor = mix(ubuf.primaryColor.rgb * 0.8, ubuf.secondaryColor.rgb, fillFactor);
|
||||
|
||||
// Boost brightness with bass
|
||||
fillColor *= 1.0 + bass * 0.3;
|
||||
|
||||
// Alpha: stronger near the edge, fades toward center
|
||||
float fillAlpha = mix(0.4, 1.0, fillFactor) * ubuf.waveThickness;
|
||||
fillAlpha = clamp(fillAlpha, 0.0, 1.0);
|
||||
|
||||
vec4 fill = vec4(fillColor, fillAlpha);
|
||||
color = max(color, fill);
|
||||
}
|
||||
|
||||
// Bright edge line at the wave boundary
|
||||
float edgeThickness = ubuf.waveThickness * 0.025;
|
||||
float distToEdge = abs(d - waveRadius);
|
||||
if (distToEdge < edgeThickness) {
|
||||
float edgeFactor = 1.0 - smoothstep(0.0, edgeThickness, distToEdge);
|
||||
vec3 edgeColor = ubuf.secondaryColor.rgb * (1.2 + smoothedAudio * 0.5);
|
||||
|
||||
// Add highlight at peaks
|
||||
if (smoothedAudio > 0.5) {
|
||||
edgeColor += vec3(0.3, 0.4, 0.5) * (smoothedAudio - 0.5);
|
||||
}
|
||||
|
||||
vec4 edge = vec4(edgeColor, edgeFactor);
|
||||
color = max(color, edge);
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
// Compute visualization color at given UV coordinates
|
||||
// Returns vec4 with RGB color and alpha
|
||||
vec4 computeVisualization(vec2 uv, float iTime, float bass, float mid, float highMid, float treble) {
|
||||
float aspect = ubuf.itemWidth / ubuf.itemHeight;
|
||||
vec2 centered = (uv - 0.5) * 2.0;
|
||||
centered.x *= aspect;
|
||||
|
||||
float theta = atan(centered.y, centered.x);
|
||||
float d = length(centered);
|
||||
float innerRadius = ubuf.innerDiameter / 2.0;
|
||||
float baseRadius = 0.35; // Fixed reference for outer extent
|
||||
|
||||
vec4 color = vec4(0.0);
|
||||
|
||||
// RING SYSTEM
|
||||
if (hasRings()) {
|
||||
// Center Waves
|
||||
if (d < innerRadius * 0.6) {
|
||||
float wave = mid * 0.8;
|
||||
float ripple = sin(d * 25.0 + wave * 15.0 - iTime * 2.0);
|
||||
if (ripple > 0.7) {
|
||||
float intensity = clamp(mid * 0.6, 0.0, 0.4);
|
||||
vec4 waveColor = ubuf.secondaryColor * intensity * ubuf.ringOpacity;
|
||||
color = max(color, waveColor);
|
||||
}
|
||||
}
|
||||
|
||||
// Energy Ring
|
||||
float energyRad = innerRadius * 0.65;
|
||||
float energyThickness = 0.015 + clamp(highMid * 0.02, 0.0, 0.03);
|
||||
if (d > energyRad - energyThickness && d < energyRad + energyThickness) {
|
||||
float segmentAngle = theta * 8.0 + highMid * 3.0 + iTime;
|
||||
if (mod(segmentAngle, 1.0) < 0.6) {
|
||||
float alpha = clamp(highMid * 2.0, 0.3, 1.0) * ubuf.ringOpacity;
|
||||
vec4 energyColor = mix(ubuf.primaryColor, ubuf.secondaryColor, 0.5) * alpha;
|
||||
color = max(color, energyColor);
|
||||
}
|
||||
}
|
||||
|
||||
// Particle Ring
|
||||
float particleRad = innerRadius * 0.75;
|
||||
if (d > particleRad - 0.02 && d < particleRad + 0.02) {
|
||||
float particleAngle = theta + treble * 2.0 + iTime * 0.5;
|
||||
float particleSpacing = TWOPI / 16.0;
|
||||
if (mod(particleAngle, particleSpacing) < 0.15) {
|
||||
float brightness = 0.5 + clamp(treble * 1.5, 0.0, 0.5);
|
||||
vec4 particleColor = ubuf.secondaryColor * brightness * ubuf.ringOpacity;
|
||||
color = max(color, particleColor);
|
||||
}
|
||||
}
|
||||
|
||||
// Tech Grid Ring
|
||||
float gridRad = innerRadius * 0.85;
|
||||
if (d > gridRad - 0.012 && d < gridRad + 0.012) {
|
||||
float gridAngle = theta + iTime * ubuf.rotationSpeed;
|
||||
float gridDensity = 0.08 + clamp(mid * 0.05, 0.0, 0.1);
|
||||
if (mod(gridAngle, 0.2) < gridDensity) {
|
||||
vec4 gridColor = ubuf.primaryColor * 0.7 * ubuf.ringOpacity;
|
||||
gridColor.rgb += vec3(0.1, 0.15, 0.2) * clamp(mid, 0.0, 0.8);
|
||||
color = max(color, gridColor);
|
||||
}
|
||||
}
|
||||
|
||||
// Accent Ring
|
||||
float accentRad = innerRadius * 0.92;
|
||||
float pulse = clamp(bass * 0.08, 0.0, 0.05);
|
||||
if (d > accentRad - pulse - 0.008 && d < accentRad + pulse + 0.015) {
|
||||
float colorShift = clamp(bass * 0.5, 0.0, 1.0);
|
||||
vec4 ringColor = mix(ubuf.secondaryColor * 0.7, ubuf.primaryColor, colorShift);
|
||||
ringColor.a = ubuf.ringOpacity;
|
||||
ringColor.rgb *= 1.0 + bass * 0.3;
|
||||
color = max(color, ringColor);
|
||||
}
|
||||
|
||||
// Outer Ring
|
||||
float outerRad = innerRadius + bass * 0.05;
|
||||
if (d > outerRad - 0.008 && d < outerRad + 0.008) {
|
||||
vec4 outerColor = ubuf.primaryColor * ubuf.ringOpacity;
|
||||
outerColor.rgb += vec3(0.2, 0.3, 0.4) * clamp(treble * 0.5, 0.0, 0.3);
|
||||
outerColor.rgb *= 1.0 + bass * 0.4;
|
||||
color = max(color, outerColor);
|
||||
}
|
||||
}
|
||||
|
||||
// CIRCULAR AUDIO BARS (64 bars, mirrored from 32 audio samples)
|
||||
if (hasBars() && d > innerRadius) {
|
||||
// Double the visual bars by using NBARS * 2
|
||||
float section = TWOPI / float(NBARS * 2);
|
||||
float center = section / 2.0;
|
||||
|
||||
float adjustedTheta = theta + PI + iTime * ubuf.rotationSpeed * 0.2;
|
||||
float m = mod(adjustedTheta, section);
|
||||
float ym = d * sin(center - m);
|
||||
|
||||
float barW = ubuf.barWidth * 0.015;
|
||||
if (abs(ym) < barW) {
|
||||
// Calculate position in the circle (0.0 to 1.0)
|
||||
float circlePos = mod(adjustedTheta, TWOPI) / TWOPI;
|
||||
// Mirror: first half (0-0.5) maps to 0-1, second half (0.5-1) maps back 1-0
|
||||
float mirroredPos = circlePos < 0.5 ? circlePos * 2.0 : (1.0 - circlePos) * 2.0;
|
||||
float v = smoothAudio(mirroredPos);
|
||||
|
||||
float wave = sin(theta * 3.0 + mid * 5.0) * clamp(mid * 0.03, 0.0, 0.05);
|
||||
v += wave;
|
||||
v = max(v, 0.0);
|
||||
|
||||
float barStart = innerRadius;
|
||||
float barEnd = baseRadius + v * 0.5; // Fixed outer extent
|
||||
|
||||
if (d >= barStart && d <= barEnd) {
|
||||
float heightFactor = (d - barStart) / max(barEnd - barStart, 0.001);
|
||||
|
||||
vec3 bottomColor = ubuf.primaryColor.rgb * 0.6;
|
||||
vec3 middleColor = ubuf.primaryColor.rgb;
|
||||
vec3 topColor = ubuf.secondaryColor.rgb;
|
||||
|
||||
vec3 barColor;
|
||||
if (heightFactor < 0.5) {
|
||||
barColor = mix(bottomColor, middleColor, heightFactor * 2.0);
|
||||
} else {
|
||||
barColor = mix(middleColor, topColor, (heightFactor - 0.5) * 2.0);
|
||||
}
|
||||
|
||||
barColor *= 1.0 + bass * 0.4;
|
||||
|
||||
if (heightFactor > 0.85) {
|
||||
barColor += vec3(0.3, 0.4, 0.5) * clamp(treble * 0.8, 0.0, 0.5);
|
||||
}
|
||||
|
||||
float edgeFactor = 1.0 - smoothstep(barW * 0.7, barW, abs(ym));
|
||||
vec4 finalBarColor = vec4(barColor, edgeFactor);
|
||||
color = max(color, finalBarColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
void main() {
|
||||
// ============================================
|
||||
// DYNAMIC CONTENT SCALING
|
||||
// ============================================
|
||||
// Calculate max possible extent from current settings to guarantee
|
||||
// nothing ever reaches the widget border.
|
||||
//
|
||||
// Max visualization radius in centered space [-1,1]:
|
||||
// Bars/Wave: baseRadius(0.35) + sensitivity * 0.5
|
||||
// Rings: innerDiameter/2 + sensitivity * 0.05 (always smaller)
|
||||
//
|
||||
// Bloom reach: exp(-dist * minDecayRate / bloomIntensity) decays to
|
||||
// < 1/255 at dist ≈ bloomIntensity * 1.0 (from solving exp decay
|
||||
// with worst-case amplitude * multiplier chain)
|
||||
//
|
||||
// contentScale maps the widget edge to ±contentScale in centered space,
|
||||
// so setting it to maxTotalRadius ensures everything fits.
|
||||
|
||||
float maxContentRadius = 0.35 + ubuf.sensitivity * 0.5;
|
||||
float maxBloomReach = ubuf.bloomIntensity * 1.0;
|
||||
float maxTotalRadius = maxContentRadius + maxBloomReach;
|
||||
float contentScale = max(maxTotalRadius * 1.05, 1.0); // 5% safety margin
|
||||
|
||||
vec2 uv = (qt_TexCoord0 - 0.5) * contentScale + 0.5;
|
||||
|
||||
// Convert linear time (0-3600) to smooth oscillation for seamless looping
|
||||
// sin() ensures perfect continuity when QML wraps from 3600 back to 0
|
||||
float iTime = sin(ubuf.time * TWOPI / 3600.0) * 1800.0 + 1800.0;
|
||||
|
||||
// Frequency analysis
|
||||
float bass = getBass();
|
||||
float mid = getMid();
|
||||
float highMid = getHighMid();
|
||||
float treble = getTreble();
|
||||
|
||||
// Get base visualization color based on mode
|
||||
// Mode 0: bars only, Mode 1: wave only, Mode 2: rings only
|
||||
// Mode 3: bars+rings, Mode 4: wave+rings, Mode 5: all
|
||||
vec4 color;
|
||||
if (hasWave() && !hasBars()) {
|
||||
// Wave only or wave+rings (modes 1, 4)
|
||||
color = computePolarWave(uv, iTime, bass, mid, highMid, treble);
|
||||
} else if (hasBars() && !hasWave()) {
|
||||
// Bars only or bars+rings (modes 0, 3)
|
||||
color = computeVisualization(uv, iTime, bass, mid, highMid, treble);
|
||||
} else if (hasWave() && hasBars()) {
|
||||
// All mode (5) - combine both
|
||||
vec4 barsColor = computeVisualization(uv, iTime, bass, mid, highMid, treble);
|
||||
vec4 waveColor = computePolarWave(uv, iTime, bass, mid, highMid, treble);
|
||||
color = max(barsColor, waveColor);
|
||||
} else {
|
||||
// Rings only (mode 2) - still need to call one of them for ring rendering
|
||||
color = computeVisualization(uv, iTime, bass, mid, highMid, treble);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// BLOOM EFFECT - Glow based on distance to geometry
|
||||
// ============================================
|
||||
|
||||
if (ubuf.bloomIntensity > 0.01 && color.a < 0.01) {
|
||||
// Only apply bloom where there's no geometry (empty space)
|
||||
// Find distance to nearest bright element
|
||||
|
||||
float aspect = ubuf.itemWidth / ubuf.itemHeight;
|
||||
vec2 centered = (uv - 0.5) * 2.0;
|
||||
centered.x *= aspect;
|
||||
float d = length(centered);
|
||||
float theta = atan(centered.y, centered.x);
|
||||
|
||||
float innerRadius = ubuf.innerDiameter / 2.0;
|
||||
float baseRadius = 0.35; // Fixed reference for outer extent
|
||||
float glowAmount = 0.0;
|
||||
vec3 glowColor = vec3(0.0);
|
||||
|
||||
// Glow from rings (if enabled)
|
||||
if (hasRings()) {
|
||||
// Outer ring glow
|
||||
float outerRad = innerRadius + bass * 0.05;
|
||||
float ringDist = abs(d - outerRad);
|
||||
float ringGlow = exp(-ringDist * 8.0 / ubuf.bloomIntensity) * (1.0 + bass * 0.5);
|
||||
glowColor += ubuf.primaryColor.rgb * ringGlow;
|
||||
glowAmount = max(glowAmount, ringGlow);
|
||||
|
||||
// Accent ring glow
|
||||
float accentRad = innerRadius * 0.92;
|
||||
float accentDist = abs(d - accentRad);
|
||||
float accentGlow = exp(-accentDist * 10.0 / ubuf.bloomIntensity) * (0.7 + bass * 0.3);
|
||||
glowColor += mix(ubuf.secondaryColor.rgb, ubuf.primaryColor.rgb, 0.5) * accentGlow;
|
||||
glowAmount = max(glowAmount, accentGlow);
|
||||
}
|
||||
|
||||
// Glow from visualization (bars or polar wave)
|
||||
if ((hasBars() || hasWave()) && d > innerRadius * 0.8) {
|
||||
float adjustedTheta = theta + PI + iTime * ubuf.rotationSpeed * 0.2;
|
||||
float circlePos = mod(adjustedTheta, TWOPI) / TWOPI;
|
||||
float mirroredPos = circlePos < 0.5 ? circlePos * 2.0 : (1.0 - circlePos) * 2.0;
|
||||
float v = smoothAudio(mirroredPos);
|
||||
|
||||
if (hasWave()) {
|
||||
// Polar wave bloom - Catmull-Rom spline with mirroring matching main render
|
||||
float mirroredPos = circlePos < 0.5 ? circlePos * 2.0 : (1.0 - circlePos) * 2.0;
|
||||
float bandPos = mirroredPos * float(NBARS - 1);
|
||||
float fband1 = floor(bandPos);
|
||||
float fband0 = max(fband1 - 1.0, 0.0);
|
||||
float fband2 = min(fband1 + 1.0, float(NBARS - 1));
|
||||
float fband3 = min(fband1 + 2.0, float(NBARS - 1));
|
||||
|
||||
float t = fract(bandPos);
|
||||
float p0 = getAudio(fband0 / float(NBARS - 1)) * ubuf.sensitivity;
|
||||
float p1 = getAudio(fband1 / float(NBARS - 1)) * ubuf.sensitivity;
|
||||
float p2 = getAudio(fband2 / float(NBARS - 1)) * ubuf.sensitivity;
|
||||
float p3 = getAudio(fband3 / float(NBARS - 1)) * ubuf.sensitivity;
|
||||
|
||||
float t2 = t * t;
|
||||
float t3 = t2 * t;
|
||||
float smoothedAudio = 0.5 * (
|
||||
(2.0 * p1) +
|
||||
(-p0 + p2) * t +
|
||||
(2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2 +
|
||||
(-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3
|
||||
);
|
||||
smoothedAudio = max(smoothedAudio, 0.0);
|
||||
|
||||
float waveRadius = baseRadius + smoothedAudio * 0.5;
|
||||
|
||||
// Glow from the filled area and edge
|
||||
float distToWave = abs(d - waveRadius);
|
||||
float waveGlow = exp(-distToWave * 8.0 / ubuf.bloomIntensity) * smoothedAudio * 2.5;
|
||||
|
||||
vec3 waveGlowColor = mix(ubuf.primaryColor.rgb, ubuf.secondaryColor.rgb, smoothedAudio);
|
||||
glowColor += waveGlowColor * waveGlow;
|
||||
glowAmount = max(glowAmount, waveGlow);
|
||||
}
|
||||
|
||||
if (hasBars()) {
|
||||
// Bars bloom
|
||||
float section = TWOPI / float(NBARS * 2);
|
||||
float m = mod(adjustedTheta, section);
|
||||
float center = section / 2.0;
|
||||
|
||||
float barAngleDist = min(abs(m - center), section - abs(m - center));
|
||||
float barEnd = baseRadius + v * 0.5; // Fixed outer extent
|
||||
|
||||
float radialDist = 0.0;
|
||||
if (d < innerRadius) {
|
||||
radialDist = innerRadius - d;
|
||||
} else if (d > barEnd) {
|
||||
radialDist = d - barEnd;
|
||||
}
|
||||
|
||||
float totalDist = length(vec2(barAngleDist * d, radialDist));
|
||||
float barGlow = exp(-totalDist * 15.0 / ubuf.bloomIntensity) * v * 2.0;
|
||||
|
||||
float heightFactor = clamp((d - innerRadius) / max(barEnd - innerRadius, 0.001), 0.0, 1.0);
|
||||
vec3 barGlowColor = mix(ubuf.primaryColor.rgb, ubuf.secondaryColor.rgb, heightFactor);
|
||||
|
||||
glowColor += barGlowColor * barGlow;
|
||||
glowAmount = max(glowAmount, barGlow);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply bloom
|
||||
float bloomMult = ubuf.bloomIntensity * (1.0 + bass * 0.5);
|
||||
color.rgb = glowColor * bloomMult;
|
||||
color.a = glowAmount * bloomMult * 0.6;
|
||||
|
||||
// Clamp to reasonable values
|
||||
color.rgb = min(color.rgb, vec3(1.5));
|
||||
color.a = min(color.a, 0.8);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// EDGE FADE - radial falloff that only affects the bloom zone
|
||||
// ============================================
|
||||
// Fade starts just past where main content ends (maxContentRadius)
|
||||
// and reaches zero at the widget edge (contentScale) in centered space.
|
||||
// This catches bloom tails without affecting the main visualization.
|
||||
|
||||
vec2 fromCenter = (qt_TexCoord0 - 0.5) * 2.0; // -1 to 1 in widget space
|
||||
float edgeProximity = max(abs(fromCenter.x), abs(fromCenter.y)); // box distance
|
||||
float fadeStart = maxContentRadius / contentScale; // where content ends in widget space
|
||||
float edgeFade = 1.0 - smoothstep(fadeStart, 1.0, edgeProximity);
|
||||
color *= edgeFade;
|
||||
|
||||
// ============================================
|
||||
// CORNER MASKING
|
||||
// ============================================
|
||||
|
||||
vec2 pixelPos = qt_TexCoord0 * vec2(ubuf.itemWidth, ubuf.itemHeight);
|
||||
vec2 centerPos = pixelPos - vec2(ubuf.itemWidth, ubuf.itemHeight) * 0.5;
|
||||
vec2 halfSize = vec2(ubuf.itemWidth, ubuf.itemHeight) * 0.5;
|
||||
float dist = roundedBoxSDF(centerPos, halfSize, ubuf.cornerRadius);
|
||||
float cornerMask = 1.0 - smoothstep(-1.0, 0.0, dist);
|
||||
|
||||
// Final output with premultiplied alpha
|
||||
float finalAlpha = color.a * ubuf.qt_Opacity * cornerMask;
|
||||
fragColor = vec4(color.rgb * finalAlpha, finalAlpha);
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user