commit 5e720d2995c0c8cbb76f638522473f3fd9396556 Author: SO_RA <490328928@qq.com> Date: Sun Mar 20 12:25:29 2022 +0800 first commit diff --git a/Locales/ja_JP.qm b/Locales/ja_JP.qm new file mode 100644 index 0000000..2abef90 Binary files /dev/null and b/Locales/ja_JP.qm differ diff --git a/Locales/zh.qm b/Locales/zh.qm new file mode 100644 index 0000000..554abf6 Binary files /dev/null and b/Locales/zh.qm differ diff --git a/Locales/zh_CN.qm b/Locales/zh_CN.qm new file mode 100644 index 0000000..49000ae Binary files /dev/null and b/Locales/zh_CN.qm differ diff --git a/Presets/Circle/preset.json b/Presets/Circle/preset.json new file mode 100644 index 0000000..8d2612c --- /dev/null +++ b/Presets/Circle/preset.json @@ -0,0 +1,4 @@ +{ + "source": "nvg://advp.widget.mashiros.top/widget", + "settings": "settings.xml" +} \ No newline at end of file diff --git a/Presets/Circle/preview.png b/Presets/Circle/preview.png new file mode 100644 index 0000000..9d1a619 Binary files /dev/null and b/Presets/Circle/preview.png differ diff --git a/Presets/Circle/settings.xml b/Presets/Circle/settings.xml new file mode 100644 index 0000000..dd66d01 --- /dev/null +++ b/Presets/Circle/settings.xml @@ -0,0 +1,8 @@ + + + {"__version":"1.0.0","__cfg_height":580,"Center Line":true,"Center Color":"#ff4500","Line Color":"#ff4500","Line Position":0,"Data Length":0,"Channel":2,"Reverse":false,"Rotate Settings":{"Center Enable":false,"Center Angle":10,"Line Enable":false,"Line Angle":10},"Data Settings":{"Auto Normalizing":true,"Amplitude":4,"Unit Style":0}} + {"index":2} + "nvg://advp.widget.mashiros.top/advp-style-preset-circle#Style.qml" + {"__version":"1.0.0","__cfg_height":580,"Line Width":1,"Line Color":"#ff4500","Data Settings":{"Auto Normalizing":true,"Amplitude":10}} + {"__version":"1.0.0","__cfg_height":740,"Main Color":"#ff4500","Line Position":0,"Line Width":1,"Max Range":50,"Data Length":0,"Channel":2,"Reverse":false,"Rotate":false,"Ratate Speed":10,"Angle":0,"Data Settings":{"Auto Normalizing":true,"Amplitude":10,"Unit Style":0}} + \ No newline at end of file diff --git a/Presets/Line/preset.json b/Presets/Line/preset.json new file mode 100644 index 0000000..8d2612c --- /dev/null +++ b/Presets/Line/preset.json @@ -0,0 +1,4 @@ +{ + "source": "nvg://advp.widget.mashiros.top/widget", + "settings": "settings.xml" +} \ No newline at end of file diff --git a/Presets/Line/preview.png b/Presets/Line/preview.png new file mode 100644 index 0000000..3435963 Binary files /dev/null and b/Presets/Line/preview.png differ diff --git a/Presets/Line/settings.xml b/Presets/Line/settings.xml new file mode 100644 index 0000000..27a3aa1 --- /dev/null +++ b/Presets/Line/settings.xml @@ -0,0 +1,6 @@ + + + {"__version":"1.0.0","__cfg_height":580,"Center Line":true,"Center Color":"#ff4500","Line Color":"#ff4500","Line Position":0,"Data Length":0,"Channel":2,"Reverse":false,"Rotate Settings":{"Center Enable":false,"Center Angle":10,"Line Enable":false,"Line Angle":10},"Data Settings":{"Auto Normalizing":true,"Amplitude":4,"Unit Style":0}} + {"index":0} + "nvg://advp.widget.mashiros.top/advp-style-preset_line#Style.qml" + \ No newline at end of file diff --git a/Presets/Ordinal_Scale_UI_bottom/preset.json b/Presets/Ordinal_Scale_UI_bottom/preset.json new file mode 100644 index 0000000..8d2612c --- /dev/null +++ b/Presets/Ordinal_Scale_UI_bottom/preset.json @@ -0,0 +1,4 @@ +{ + "source": "nvg://advp.widget.mashiros.top/widget", + "settings": "settings.xml" +} \ No newline at end of file diff --git a/Presets/Ordinal_Scale_UI_bottom/preview.png b/Presets/Ordinal_Scale_UI_bottom/preview.png new file mode 100644 index 0000000..cb0105f Binary files /dev/null and b/Presets/Ordinal_Scale_UI_bottom/preview.png differ diff --git a/Presets/Ordinal_Scale_UI_bottom/settings.xml b/Presets/Ordinal_Scale_UI_bottom/settings.xml new file mode 100644 index 0000000..1ef0a17 --- /dev/null +++ b/Presets/Ordinal_Scale_UI_bottom/settings.xml @@ -0,0 +1,10 @@ + + + {"__version":"1.0.0","__cfg_height":580,"Center Line":true,"Center Color":"#ff4500","Line Color":"#ff4500","Line Position":0,"Data Length":0,"Channel":2,"Reverse":false,"Rotate Settings":{"Center Enable":false,"Center Angle":10,"Line Enable":false,"Line Angle":10},"Data Settings":{"Auto Normalizing":true,"Amplitude":4,"Unit Style":0}} + {"index":4} + "nvg://advp.widget.mashiros.top/advp-style-preset-ordinal_scale_ui_bottom#Style.qml" + {"__version":"1.0.0","__cfg_height":580,"Line Width":1,"Line Color":"#ff4500","Data Settings":{"Auto Normalizing":true,"Amplitude":10}} + {"__version":"1.0.0","__cfg_height":740,"Main Color":"#ff4500","Line Position":0,"Line Width":1,"Max Range":50,"Data Length":0,"Channel":2,"Reverse":false,"Rotate":false,"Ratate Speed":10,"Angle":0,"Data Settings":{"Auto Normalizing":true,"Amplitude":10,"Unit Style":0}} + {"__version":"1.0.0","__cfg_height":740,"Main Color":"#ff4500","Line Position":0,"Line Width":1,"Max Range":50,"Data Length":0,"Channel":2,"Reverse":false,"Rotate":false,"Ratate Speed":10,"Angle":0,"Data Settings":{"Auto Normalizing":true,"Amplitude":10,"Unit Style":0}} + {"__version":"1.0.0","__cfg_height":710,"Bass Color":"#dc143c","Alto Color":"#f8f8ff","Treble Color":"#4169e1","Bass AM":100,"Alto AM":150,"Treble AM":200,"Static AM":15,"Speed":20,"Data Settings":{"Auto Normalizing":true,"Amplitude":10}} + \ No newline at end of file diff --git a/Presets/Solidcircle/preset.json b/Presets/Solidcircle/preset.json new file mode 100644 index 0000000..8d2612c --- /dev/null +++ b/Presets/Solidcircle/preset.json @@ -0,0 +1,4 @@ +{ + "source": "nvg://advp.widget.mashiros.top/widget", + "settings": "settings.xml" +} \ No newline at end of file diff --git a/Presets/Solidcircle/preview.png b/Presets/Solidcircle/preview.png new file mode 100644 index 0000000..e13d40c Binary files /dev/null and b/Presets/Solidcircle/preview.png differ diff --git a/Presets/Solidcircle/settings.xml b/Presets/Solidcircle/settings.xml new file mode 100644 index 0000000..041b0b2 --- /dev/null +++ b/Presets/Solidcircle/settings.xml @@ -0,0 +1,9 @@ + + + {"__version":"1.0.0","__cfg_height":580,"Center Line":true,"Center Color":"#ff4500","Line Color":"#ff4500","Line Position":0,"Data Length":0,"Channel":2,"Reverse":false,"Rotate Settings":{"Center Enable":false,"Center Angle":10,"Line Enable":false,"Line Angle":10},"Data Settings":{"Auto Normalizing":true,"Amplitude":4,"Unit Style":0}} + {"index":3} + "nvg://advp.widget.mashiros.top/advp-style-preset-solidcircle#Style.qml" + {"__version":"1.0.0","__cfg_height":580,"Line Width":1,"Line Color":"#ff4500","Data Settings":{"Auto Normalizing":true,"Amplitude":10}} + {"__version":"1.0.0","__cfg_height":740,"Main Color":"#ff4500","Line Position":0,"Line Width":1,"Max Range":50,"Data Length":0,"Channel":2,"Reverse":false,"Rotate":false,"Ratate Speed":10,"Angle":0,"Data Settings":{"Auto Normalizing":true,"Amplitude":10,"Unit Style":0}} + {"__version":"1.0.0","__cfg_height":740,"Main Color":"#ff4500","Line Position":0,"Line Width":1,"Max Range":50,"Data Length":0,"Channel":2,"Reverse":false,"Rotate":false,"Ratate Speed":10,"Angle":0,"Data Settings":{"Auto Normalizing":true,"Amplitude":10,"Unit Style":0}} + \ No newline at end of file diff --git a/Presets/Waves/preset.json b/Presets/Waves/preset.json new file mode 100644 index 0000000..8d2612c --- /dev/null +++ b/Presets/Waves/preset.json @@ -0,0 +1,4 @@ +{ + "source": "nvg://advp.widget.mashiros.top/widget", + "settings": "settings.xml" +} \ No newline at end of file diff --git a/Presets/Waves/preview.png b/Presets/Waves/preview.png new file mode 100644 index 0000000..3c2536d Binary files /dev/null and b/Presets/Waves/preview.png differ diff --git a/Presets/Waves/settings.xml b/Presets/Waves/settings.xml new file mode 100644 index 0000000..3b3c57c --- /dev/null +++ b/Presets/Waves/settings.xml @@ -0,0 +1,7 @@ + + + {"__version":"1.0.0","__cfg_height":580,"Center Line":true,"Center Color":"#ff4500","Line Color":"#ff4500","Line Position":0,"Data Length":0,"Channel":2,"Reverse":false,"Rotate Settings":{"Center Enable":false,"Center Angle":10,"Line Enable":false,"Line Angle":10},"Data Settings":{"Auto Normalizing":true,"Amplitude":4,"Unit Style":0}} + {"index":1} + "nvg://advp.widget.mashiros.top/advp-style-preset_waves#Style.qml" + {"__version":"1.0.0","__cfg_height":580,"Line Width":1,"Line Color":"#ff4500","Data Settings":{"Auto Normalizing":true,"Amplitude":10}} + \ No newline at end of file diff --git a/bin/ADVServer.exe b/bin/ADVServer.exe new file mode 100644 index 0000000..32bd3d9 Binary files /dev/null and b/bin/ADVServer.exe differ diff --git a/bin/advConfig.ini b/bin/advConfig.ini new file mode 100644 index 0000000..5120cea --- /dev/null +++ b/bin/advConfig.ini @@ -0,0 +1,13 @@ +[Server] +ip = local +port = 5055 +maxClient = 10 +logger = true + +[FFT] +attack = 5 +decay = 5 +norspeed = 1 +peakthr = 10 +fps = 35 +changeSpeed = 20 \ No newline at end of file diff --git a/module.qml b/module.qml new file mode 100644 index 0000000..a9eda1d --- /dev/null +++ b/module.qml @@ -0,0 +1,23 @@ +import NERvGear 1.0 as NVG +import NERvGear.Private 1.0 as NVGP + +import "./qml" + +NVG.Module { + initialize: function () { + console.log("Initializing ADV-Plugin."); + Common.execute("../bin/ADVServer.exe", "-reboot"); + Common.setWsocket(true); + return true; + } + + ready: function () { + console.log("ADV-Plugin is ready."); + } + + cleanup: function () { + console.log("Cleaning up ADV-Plugin."); + Common.setWsocket(false); + Common.execute("../bin/ADVServer.exe", "-close"); + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..8ca4274 --- /dev/null +++ b/package.json @@ -0,0 +1,144 @@ +{ + "name": "top.mashiros.widget.advp", + "version": "1.0.0", + + "title": { + "en": "ADV Plugin", + "zh": "音頻可視化插件", + "zh_CN": "音频可视化插件", + "ja_JP": "音声視覚化プラグイン" + }, + + "description": { + "en": "Audio visualization plugin for SAO Utils", + "zh": "用于SAO Utils 的音頻可視化插件", + "zh_CN": "用于SAO Utils的音频可视化插件", + "ja_JP": "SAO Utils用の音声視覚化プラグイン" + }, + + "homepage": "https://nvg.dev/Mashiro_Sorata/ADV-Plugin", + + "bugs": { + "email": "mashiro_sorata@qq.com", + "url": "https://nvg.dev/Mashiro_Sorata/ADV-Plugin/issues" + }, + + "author": { + "name": "Mashiro_Sorata", + "email": "mashiro_sorata@qq.com", + "url": "https://www.mashiros.top/" + }, + + "engines": { + "qt": "~5", + "qt.quick": ">=2.12", + "nvg.api": "~1" + }, + + "main": "module.qml", + + "resources": [ + { + "location": "/widget", + "catalog": "widget", + "title": { + "en": "ADV Widget", + "zh": "音頻可視化挂件", + "zh_CN": "音频可视化挂件", + "ja_JP": "音声視覚化プラグイン" + }, + "preview": "preview.png", + "entry": "qml/Main.qml" + }, + { + "location": "/advp-style-preset_line", + "catalog": "top.mashiros.advp-style", + "title": "Preset Line", + "entry": "./styles/Preset_line" + }, + { + "location": "/advp-style-preset_waves", + "catalog": "top.mashiros.advp-style", + "title": "Preset Waves", + "entry": "./styles/Preset_waves" + }, + { + "location": "/advp-style-preset-circle", + "catalog": "top.mashiros.advp-style", + "title": "Preset Circle", + "entry": "./styles/Preset_circle" + }, + { + "location": "/advp-style-preset-solidcircle", + "catalog": "top.mashiros.advp-style", + "title": "Preset Solid-circle", + "entry": "./styles/Preset_solidcircle" + }, + { + "location": "/advp-style-preset-ordinal_scale_ui_bottom", + "catalog": "top.mashiros.advp-style", + "title": "Preset Ordinal Scale UI bottom", + "entry": "./styles/Preset_Ordinal_Scale_UI_bottom" + }, + { + "location": "/preset/advp-style-preset_line", + "catalog": "preset/widget", + "title": { + "en": "ADV Preset Line", + "zh": "ADV預置 Line", + "zh_CN": "ADV预置 Line", + "ja_JP": "ADVプリセット Line" + }, + "preview": "Presets/Line/preview.png", + "entry": "Presets/Line/preset.json" + }, + { + "location": "/preset/advp-style-preset_waves", + "catalog": "preset/widget", + "title": { + "en": "ADV Preset Waves", + "zh": "ADV預置 Waves", + "zh_CN": "ADV预置 Waves", + "ja_JP": "ADVプリセット Waves" + }, + "preview": "Presets/Waves/preview.png", + "entry": "Presets/Waves/preset.json" + }, + { + "location": "/preset/advp-style-preset_circle", + "catalog": "preset/widget", + "title": { + "en": "ADV Preset Circle", + "zh": "ADV預置 Circle", + "zh_CN": "ADV预置 Circle", + "ja_JP": "ADVプリセット Circle" + }, + "preview": "Presets/Circle/preview.png", + "entry": "Presets/Circle/preset.json" + }, + { + "location": "/preset/advp-style-preset_solidcircle", + "catalog": "preset/widget", + "title": { + "en": "ADV Preset Solid-circle", + "zh": "ADV預置 Solid-circle", + "zh_CN": "ADV预置 Solid-circle", + "ja_JP": "ADVプリセット Solid-circle" + }, + "preview": "Presets/Solidcircle/preview.png", + "entry": "Presets/Solidcircle/preset.json" + }, + { + "location": "/preset/advp-style-preset_ordinal_scale_ui_bottom", + "catalog": "preset/widget", + "title": { + "en": "ADV Preset Ordinal Scale UI bottom", + "zh": "ADV預置 序列之爭底部UI", + "zh_CN": "ADV预置 序列之争底部UI", + "ja_JP": "ADVプリセット オーディナル・スケール- UI 下部" + }, + "preview": "Presets/Ordinal_Scale_UI_bottom/preview.png", + "entry": "Presets/Ordinal_Scale_UI_bottom/preset.json" + } + ] +} \ No newline at end of file diff --git a/preview.png b/preview.png new file mode 100644 index 0000000..3435963 Binary files /dev/null and b/preview.png differ diff --git a/qml/Common.qml b/qml/Common.qml new file mode 100644 index 0000000..ac4ab43 --- /dev/null +++ b/qml/Common.qml @@ -0,0 +1,133 @@ +pragma Singleton +import QtQml 2.2 +import QtQuick 2.7 + +import NERvGear 1.0 as NVG + + + +Item { + readonly property var styles: [] + readonly property var stylesURL: [] + readonly property var stylesCFG: [] + + signal audioDataUpdated(var audioData) + signal wsocketClosed() + + function execute(path, args) { + path = NVG.Url.toLocalFile(Qt.resolvedUrl(path)); + NVG.SystemCall.execute(path, args); + } + + function openFile(fileUrl) { + let request = new XMLHttpRequest(); + request.open("GET", fileUrl, false); + request.send(null); + let data = request.responseText; + request = null; + return data; + } + + function parseINIString(data){ + let regex = { + section: /\[\s*([^]*)\s*\]\s*$/, + param: /^\s*([\w\.\-\_]+)\s*=\s*(.*?)\s*$/, + comment: /^\s*;.*$/ + }; + let value = {}; + let lines = data.split(/\r\n|\r|\n/); + let section = null; + let match; + lines.forEach(function(line){ + if(regex.comment.test(line)){ + return; + }else if(regex.param.test(line)){ + match = line.match(regex.param); + if(section){ + value[section][match[1]] = match[2]; + }else{ + value[match[1]] = match[2]; + } + }else if(regex.section.test(line)){ + match = line.match(regex.section); + value[match[1]] = {}; + section = match[1]; + }else if(line.length === 0 && section){ + section = null; + }; + }); + return value; + } + + Loader { + id: wsocket + sourceComponent: WSocket { + Component.onCompleted: { + let ini_data = openFile("../bin/advConfig.ini"); + ini_data = ini_data.toLowerCase(); + let cfg = parseINIString(ini_data); + if(cfg["server"]["ip"].toLowerCase() === "local") { + wsIp = "localhost"; + } else { + wsIp = cfg["server"]["ip"]; + } + wsPort = cfg["server"]["port"]; + }} + active: false + } + + function setWsocket(status) { + wsocket.active = status; + } + + onWsocketClosed: { + if (wsocket.active) { + console.log("Try to reboot ADVServer..."); + execute("../bin/ADVServer.exe", "-reboot"); + wsocket.active = false; + wsocket.active = true; + } + } + + function parse_resource(resource_list, sort) { + if (sort) + resource_list.sort(function (x, y) { + let preset_order = ["Preset Line", "Preset Waves", "Preset Circle", "Preset Solid-circle", "Preset Ordinal Scale UI bottom"]; + if (preset_order.indexOf(x.title) < preset_order.indexOf(y.title)) + return -1; + else if(preset_order.indexOf(x.title) > preset_order.indexOf(y.title)) + return 1; + else + return 0; + }); + resource_list.forEach(function (resource) { + let name = resource.title; + let styleURL = ""; + let styleCFG = ""; + resource.files().forEach(function (file) { + if (file.entry === "Style.qml") { + styleURL = String(file.url); + } else if (file.entry === "Config.qml") { + styleCFG = String(file.url); + } + }); + if (styleURL && stylesURL.indexOf(styleURL) === -1) { + styles.push(name); + stylesURL.push(styleURL); + stylesCFG.push(styleCFG); + + } +// console.log(JSON.stringify(Object.keys(resource), null, 2)); +// console.log(JSON.stringify(resource.files(), null, 2)); + }); + } + + Component.onCompleted: { + const preset_list = NVG.Resources.filter(/advp.widget.mashiros.top/, /top.mashiros.advp-style/); + parse_resource(preset_list, true); + const third_list = NVG.Resources.filter(/.*/, /top.mashiros.advp-style/); + parse_resource(third_list, false); +// console.log(JSON.stringify(styles, null, 2)); +// console.log(JSON.stringify(stylesURL, null, 2)); + } +} diff --git a/qml/Main.qml b/qml/Main.qml new file mode 100644 index 0000000..47f1947 --- /dev/null +++ b/qml/Main.qml @@ -0,0 +1,60 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 + +import NERvGear 1.0 as NVG +import NERvGear.Templates 1.0 as T + +import "." + + +T.Widget { + id: widget + solid: true + title: qsTr("ADV Widget") + resizable: true + + editing: styleDialog.active + + property bool initial: true + + function setStyleURL(url) { + styleLoader.source = url; + } + + Loader { + id: styleDialog + active: false + visible: false + sourceComponent: StylePreferences { + transientParent: widget.NVG.View.window + } + + onLoaded: { + if(initial) { + styleDialog.active = false; + styleDialog.visible = true; + initial = false; + } else { + item.visible = true; + } + } + } + + Loader { + id: styleLoader + active: widget.NVG.View.exposed + enabled: true + source: "" + } + + menu: Menu { + Action { + text: qsTr("Settings") + onTriggered: styleDialog.active = true + } + } + + Component.onCompleted: { + styleDialog.active = true; + } +} diff --git a/qml/StylePreferences.qml b/qml/StylePreferences.qml new file mode 100644 index 0000000..6a2e421 --- /dev/null +++ b/qml/StylePreferences.qml @@ -0,0 +1,181 @@ +import QtQuick 2.12 + +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 +import NERvGear 1.0 as NVG +import NERvGear.Preferences 1.0 as P + +import "." + + +NVG.Window { + id: window + title: qsTr("ADV-Plugin: Settings") + visible: false + minimumWidth: 480 + minimumHeight: 580 + maximumWidth: minimumWidth + maximumHeight: minimumHeight + width: minimumWidth + height: minimumHeight + + Behavior on minimumHeight { + PropertyAnimation { + duration: 500 + easing.type: Easing.InOutBack + } + } + + property var configuration + property var old_style_cfg + + ColumnLayout { + id: root + anchors.fill: parent + anchors.margins: 16 + anchors.topMargin: 0 + + Row { + spacing: 350 + + ToolButton { + text: qsTr("Save") + onClicked: { + configuration = rootPreference.save(); + let index = configuration["index"]; + widget.settings[Common.stylesURL[index]] = configuration[Common.stylesURL[index]]; + delete configuration[Common.stylesURL[index]]; + widget.settings.styles = configuration; + widget.settings.current_style = Common.stylesURL[index]; + styleDialog.active = false; + } + } + + ToolButton { + text: qsTr("Reset") + onClicked: { + styleLoader.load(); + let cfg = rootPreference.save(); + let index = cfg["index"]; + widget.settings[Common.stylesURL[index]] = cfg[Common.stylesURL[index]]; + widget.setStyleURL(""); + widget.setStyleURL(Qt.resolvedUrl(Common.stylesURL[widget.settings.styles["index"]])); + } + } + } + + Label { + Layout.alignment: Qt.AlignCenter + text: qsTr("Settings") + font.pixelSize: 24 + } + + Flickable { + Layout.fillWidth: true + Layout.fillHeight: true + + clip: true + contentWidth: preferenceLayout.implicitWidth + contentHeight: preferenceLayout.implicitHeight + + ColumnLayout { + id: preferenceLayout + width: root.width + + P.PreferenceGroup { + id: rootPreference + Layout.fillWidth: true + + label: qsTr("Configuration") + + onPreferenceEdited: { + let cfg = rootPreference.save(); + // console.log(JSON.stringify(cfg, null, 2)); + let index = cfg["index"]; + if (widget.settings.styles["index"] !== index) { + widget.setStyleURL(""); + widget.settings[Common.stylesURL[widget.settings.styles["index"]]] = old_style_cfg; + old_style_cfg = widget.settings[Common.stylesURL[index]]; + } + + widget.settings[Common.stylesURL[index]] = cfg[Common.stylesURL[index]]; + delete cfg[Common.stylesURL[index]]; + widget.settings.styles = cfg; + widget.setStyleURL(Qt.resolvedUrl(Common.stylesURL[index])); + } + + P.SelectPreference { + id: styleList + name: "index" + label: qsTr("Styles") + icon.name: "solid:\uf1fc" + defaultValue: 0 + model: Common.styles + } + + P.Separator {} + + P.PreferenceLoader { + id: styleLoader + name: Common.stylesURL[styleList.value] + source: Qt.resolvedUrl(Common.stylesCFG[styleList.value]) + onLoaded: { + let cfg = save(); + if (!widget.settings[Common.stylesURL[styleList.value]]) { + widget.settings[Common.stylesURL[styleList.value]] = cfg; + } else if(widget.settings[Common.stylesURL[styleList.value]]["__version"] === cfg["__version"]) { + load(widget.settings[Common.stylesURL[styleList.value]]); + } else { + widget.settings[Common.stylesURL[styleList.value]] = cfg; + } + window.minimumHeight = cfg["__cfg_height"]; + } + + onContentItemChanged: { + if(contentItem) { + contentItem.label = Common.styles[styleList.value]; + } + } + } + + P.Separator {} + + Component.onCompleted: { + if(!widget.settings.styles) { + configuration = rootPreference.save(); + let index = configuration["index"]; + widget.settings[Common.stylesURL[index]] = configuration[Common.stylesURL[index]]; + old_style_cfg = configuration[Common.stylesURL[index]]; + delete configuration[Common.stylesURL[index]]; + widget.settings.current_style = Common.stylesURL[index]; + widget.settings.styles = configuration; + } + + let index = Common.stylesURL.indexOf(widget.settings.current_style); + if (index === -1) { + index = 0; + widget.settings.current_style = Common.stylesURL[index]; + } + + widget.settings.styles["index"] = index; + widget.setStyleURL(Qt.resolvedUrl(Common.stylesURL[widget.settings.styles["index"]])); + + rootPreference.load(widget.settings.styles); + configuration = widget.settings.styles; + old_style_cfg = widget.settings[Common.stylesURL[index]]; + + styleLoader.load(widget.settings[Common.stylesURL[index]]); + } + } + } + } + } + + onClosing: { + widget.setStyleURL(""); + widget.settings[Common.stylesURL[widget.settings.styles["index"]]] = old_style_cfg; + widget.settings.styles = configuration; + widget.setStyleURL(Qt.resolvedUrl(Common.stylesURL[widget.settings.styles["index"]])); + styleDialog.active = false; + } +} diff --git a/qml/WSocket.qml b/qml/WSocket.qml new file mode 100644 index 0000000..dfe2cf2 --- /dev/null +++ b/qml/WSocket.qml @@ -0,0 +1,25 @@ +import QtWebSockets 1.1 + +import "." + + +WebSocket { + property string wsIp: "localhost" + property int wsPort: 5050 + + url: "ws://" + wsIp + ":" + wsPort + active: true + + onStatusChanged: { + if(status === WebSocket.Closed || status === WebSocket.Error) { + Common.wsocketClosed(); + } + } + + onBinaryMessageReceived: { + let arrayBuffer = new Float32Array(message); +// Common.audioData = arrayBuffer.slice(); + Common.audioDataUpdated(arrayBuffer.slice()); +// arrayBuffer = null; + } +} diff --git a/qml/api/CfgAPI.qml b/qml/api/CfgAPI.qml new file mode 100644 index 0000000..5eeaee0 --- /dev/null +++ b/qml/api/CfgAPI.qml @@ -0,0 +1,41 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import NERvGear.Preferences 1.0 as P + +P.DialogPreference { + icon.name: "solid:\uf085" + live: true + property string version: "" + property int cfg_height: 580 + + P.TextFieldPreference { + name: "__version" + visible: false + enabled: false + defaultValue: version + } + + P.SpinPreference { + name: "__cfg_height" + enabled: false + visible: false + editable: false + from: cfg_height + to: cfg_height + defaultValue: cfg_height + } + + ItemDelegate { + text: qsTr("Version") + visible: version + + Label { + anchors.right: parent.right + anchors.rightMargin: 16 + anchors.verticalCenter: parent.verticalCenter + + text: version + font.weight: Font.DemiBold + } + } +} diff --git a/qml/api/StyleAPI.qml b/qml/api/StyleAPI.qml new file mode 100644 index 0000000..b044f63 --- /dev/null +++ b/qml/api/StyleAPI.qml @@ -0,0 +1,33 @@ +import QtQuick 2.12 + +import ".." //显式导入单例Common.qml + +Canvas { + width: widget.width; + height: widget.height; + contextType: "2d"; + renderTarget: Canvas.FramebufferObject + renderStrategy: Canvas.Cooperative + + signal audioDataUpdeted(var data) + signal configsUpdated() + property var configs: widget.settings[Common.stylesURL[widget.settings.styles["index"]]] + + onConfigsChanged: { + if (context) { + configsUpdated(); + } + } + + onContextChanged: { + if (context) { + configsUpdated(); + } + } + + Connections { + enabled: Boolean(context) + target: Common + onAudioDataUpdated: audioDataUpdeted(audioData) + } +} diff --git a/qml/qmldir b/qml/qmldir new file mode 100644 index 0000000..f7640a6 --- /dev/null +++ b/qml/qmldir @@ -0,0 +1 @@ +singleton Common 1.0 Common.qml \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..c6a1e7d --- /dev/null +++ b/readme.md @@ -0,0 +1,65 @@ +

ADV-Plugin

+
+
Powered By Mashiro_Sorata
+ +--- + +# 简介 + +`ADV-Plugin`是[SAO Utils 2](http://sao.gpbeta.com/)的第三方插件,可以提供系统音频数据的可视化服务。 +得益于SAO Utils 2允许用户使用qml脚本编写扩展插件,相对于[第一代](https://github.com/Mashiro-Sorata/AudioDVServer-Plugin)的插件方案,第二代可以整合第一代中客户端与服务器端的功能,无需复杂的配置即可使用。 + +## 特色 +* 整合客户端与服务器端的功能,使用更简单 +* 预设4种频谱显示形式,其中每种都可以进一步自定义设置其样式 +* 提供了Style的开发接口,供开发者添加更多的可视化样式 +* 导入第三方Style类似其他插件,预计支持steam创意工坊下载 +* 服务端崩溃后自启动 + +## 使用说明 + +安装并启用插件后,默认加载第一种预设Style样式。右键挂件可调出菜单,在挂件菜单的挂件名选单中点击Settings选项,呼出Style设置窗口。点击其中的Styles选项可切换不同的Style风格,若此Style提供可配置项,则在Styles选项下方会出现配置界面的入口。 + +
+ +# 进阶设置 + +样式设置可在插件内设置,一般服务端设置采用默认设置即可,但也提供了服务端设置的接口作为高级设置。可通过更改本插件目录`bin`文件夹中的`advConfig.ini`文件来配置插件服务器与数据设置。当配置数据错误或无配置文件时使用默认值,配置值不区分大小写。其参数的具体说明如下。 + +- [Server] + - `ip`:可选,默认值为`local`,指代地址127.0.0.1,可更改为`any`,指代地址0.0.0.0。只支持`any`与默认参数`local`,定义插件提供服务的地址。 + - `port`:可选,默认值为`5050`,定义插件提供服务的端口号。 + - `maxClient`:可选,默认值为`5`,定义WebSocket的最大连接数。 + - `logger`:可选,默认值为`false`。设置为`true`后可在插件所在目录下输出日志文件`ADV_Log.log`。 +- [FFT] + - `attack`:可选,默认值为25。可调节频谱数据增大时的速度,该值越大,增大速度越慢。 + - `decay`:可选,默认值为25。可调节频谱数据减小时的速度,该值越大,减小速度越慢。 + - `norspeed`:可选,默认值为1。动态归一化系数,取值范围从1~99,该值越大,归一化的峰值数据收敛速度越快。 + - `peakthr`:可选,默认值为10。归一化的峰值数据的额外增量。 + - `fps`:可选,默认值为30。每秒钟数据发送的次数,**必须确保该值大于5**。 + - `changeSpeed`:可选,默认值为25。按照`changeSpeed/fps`的比例调节频谱数据变化速度,**一般该值小于fps**。 + + +`advConfig.ini` 文件示例: +```ini +[Server] +ip = local +port = 5050 +maxClient = 5 +logger = true + +[FFT] +attack = 25 +decay = 25 +norspeed = 1 +peakthr = 10 +fps = 35 +changeSpeed = 25 +``` + +# 频谱样式开发 + +如果想开发新的频谱样式,可以参照[ADV-Plugin Wiki](https://github.com/Mashiro-Sorata/ADV-Plugin/wiki)的说明及开发教程。 + +欢迎开发更多有趣好玩的频谱样式,与大家分享~ + diff --git a/styles/Preset_Ordinal_Scale_UI_bottom/Config.qml b/styles/Preset_Ordinal_Scale_UI_bottom/Config.qml new file mode 100644 index 0000000..358f2d2 --- /dev/null +++ b/styles/Preset_Ordinal_Scale_UI_bottom/Config.qml @@ -0,0 +1,111 @@ +import QtQuick 2.12 +import NERvGear.Preferences 1.0 as P + +import "../../qml/api" //导入CfgAPI.qml + +CfgAPI { + version: "1.0.0" + cfg_height: 710 + + P.ColorPreference { + name: "Bass Color" + label: qsTr("Bass Line Color") + defaultValue: "#DC143C" + } + + P.ColorPreference { + name: "Alto Color" + label: qsTr("Alto Line Color") + defaultValue: "#F8F8FF" + } + + P.ColorPreference { + name: "Treble Color" + label: qsTr("Treble Line Color") + defaultValue: "#4169E1" + } + + P.Separator {} + + P.SliderPreference { + name: "Bass AM" + label: qsTr("Bass Amplitude") + from: 10 + to: 300 + stepSize: 5 + defaultValue: 100 + displayValue: value + "%" + } + + P.SliderPreference { + name: "Alto AM" + label: qsTr("Alto Amplitude") + from: 10 + to: 300 + stepSize: 5 + defaultValue: 150 + displayValue: value + "%" + } + + P.SliderPreference { + name: "Treble AM" + label: qsTr("Treble Amplitude") + from: 10 + to: 300 + stepSize: 5 + defaultValue: 200 + displayValue: value + "%" + } + + P.Separator {} + + P.SliderPreference { + name: "Static AM" + label: qsTr("Static Amplitude") + from: 5 + to: 100 + stepSize: 1 + defaultValue: 15 + displayValue: value + "%" + } + + P.Separator {} + + P.SliderPreference { + name: "Speed" + label: qsTr("Wave Speed") + from: 1 + to: 100 + stepSize: 1 + defaultValue: 20 + displayValue: value + "%" + } + + P.Separator {} + + P.DialogPreference { + name: "Data Settings" + label: qsTr("Data Settings") + live: true + icon.name: "regular:\uf1de" + + P.SwitchPreference { + id: _cfg_preset_osui_dataSettings_autoNormalizing + name: "Auto Normalizing" + label: qsTr("Auto Normalizing") + defaultValue: true + } + + P.SpinPreference { + name: "Amplitude" + label: qsTr("Amplitude Ratio") + enabled: !_cfg_preset_osui_dataSettings_autoNormalizing.value + message: "1 to 100" + display: P.TextFieldPreference.ExpandLabel + editable: true + from: 1 + to: 100 + defaultValue: 10 + } + } +} diff --git a/styles/Preset_Ordinal_Scale_UI_bottom/Style.qml b/styles/Preset_Ordinal_Scale_UI_bottom/Style.qml new file mode 100644 index 0000000..dd408f8 --- /dev/null +++ b/styles/Preset_Ordinal_Scale_UI_bottom/Style.qml @@ -0,0 +1,123 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 + +import "../../qml/api" + +StyleAPI { + readonly property var audioData: new Array(128) + + //configs + readonly property real bassAmRatio: configs["Bass AM"]/100 + readonly property real altoAmRatio: configs["Alto AM"]/100 + readonly property real trebleAmRatio: configs["Treble AM"]/100 + readonly property string bassColor: configs["Bass Color"] + readonly property string altoColor: configs["Alto Color"] + readonly property string trebleColor: configs["Treble Color"] + readonly property bool autoNormalizing: configs["Data Settings"]["Auto Normalizing"] + readonly property real amplitude: configs["Data Settings"]["Amplitude"] / 400 + + readonly property real halfWidth: width/2 + readonly property real rHeight: 0.6*height + + readonly property real this_speed: configs["Speed"]/100 + readonly property real this_MAX: (height/2)-4 + readonly property real this_noise: configs["Static AM"]/100*this_MAX + readonly property real this_delta: this_MAX-this_noise + property real this_phase: 0 + + readonly property var _globalAttenuationArray: [0.0017856573042805614, 0.002090029027424354, 0.0024428709787506155, 0.0028511620549328143, 0.003322734461672416, 0.0038663486688144872, 0.004491769417010894, 0.0052098416088514335, 0.0060325646905618185, 0.006973163895798498, 0.008046156486762173, 0.009267410897150058, 0.010654196465028541, 0.012225221251394174, 0.014000655282882916, 0.016002136446407085, 0.018252756211446522, 0.0207770223741957, 0.023600796118042038, 0.026751200876932862, 0.03025650078017051, 0.034145946854542274, 0.038449589664666724, 0.04319805768336597, 0.048422301394722714, 0.054153303932507744, 0.06042175993022312, 0.06725772518564865, 0.07469024069755677, 0.08274693558633812, 0.09145361433175414, 0.10083383461611592, 0.11090848281544048, 0.12169535480100475, 0.13320875016810133, 0.14545908827055729, 0.15845255448710202, 0.1721907849641367, 0.18667059766189825, 0.20188377687903944, 0.21781691755482288, 0.23445133456773634, 0.2517630409920839, 0.26972279787490494, 0.28829623659542597, 0.3074440533134911, 0.32712227344992756, 0.3472825826188692, 0.36787271899666724, 0.38883692080749416, 0.4101164214705023, 0.43164998401902316, 0.4533744656929005, 0.4752254031360044, 0.4971376084092084, 0.5190457660525851, 0.5408850216893105, 0.562591553140178, 0.5841031156880014, 0.6053595539666616, 0.6263032739177719, 0.6468796693242911, 0.6670374985596821, 0.6867292083486696, 0.7059112024886071, 0.7245440545987721, 0.7425926650223671, 0.760026362980347, 0.7768189559498792, 0.7929487290003452, 0.8083983974579334, 0.8231550167821777, 0.8372098539242965, 0.8505582247014246, 0.8631993018695427, 0.875135898619801, 0.8863742321691508, 0.8969236719785354, 0.9067964769231723, 0.9160075254728139, 0.9245740426279158, 0.9325153270127456, 0.9398524811601155, 0.9466081476449091, 0.9528062533442774, 0.9584717637292349, 0.9636304487320935, 0.9683086613922199, 0.9725331301632185, 0.9763307654708479, 0.9797284808449854, 0.9827530287117202, 0.9854308507237252, 0.9877879423279077, 0.9898497311182406, 0.9916409683972773, 0.9931856332704053, 0.9945068485207086, 0.9956268074571305, 0.9965667108926166, 0.9973467133897048, 0.997985877906754, 0.9985021379865716, 0.9989122666488067, 0.9992318511763106, 0.9994752730222837, 0.9996556921079202, 0.9997850348281199, 0.9998739851348633, 0.999931978122665, 0.9999671955978632, 0.999986563172305, 0.9999957484820413, 0.9999991601923922, 0.9999999475120023, 1.0, 0.9999999475120023, 0.9999991601923922, 0.9999957484820413, 0.999986563172305, 0.9999671955978632, 0.999931978122665, 0.9998739851348633, 0.9997850348281199, 0.9996556921079202, 0.9994752730222837, 0.9992318511763106, 0.9989122666488067, 0.9985021379865716, 0.997985877906754, 0.9973467133897048, 0.9965667108926166, 0.9956268074571305, 0.9945068485207086, 0.9931856332704053, 0.9916409683972773, 0.9898497311182406, 0.9877879423279077, 0.9854308507237252, 0.9827530287117202, 0.9797284808449854, 0.9763307654708498, 0.9725331301632185, 0.9683086613922199, 0.9636304487320935, 0.9584717637292349, 0.9528062533442774, 0.9466081476449091, 0.9398524811601155, 0.9325153270127456, 0.9245740426279158, 0.9160075254728139, 0.9067964769231723, 0.8969236719785354, 0.8863742321691508, 0.875135898619801, 0.8631993018695436, 0.8505582247014246, 0.8372098539242965, 0.8231550167821794, 0.8083983974579334, 0.7929487290003452, 0.7768189559498792, 0.760026362980347, 0.7425926650223671, 0.7245440545987721, 0.7059112024886071, 0.6867292083486696, 0.6670374985596821, 0.6468796693242911, 0.6263032739177719, 0.6053595539666616, 0.5841031156880014, 0.562591553140178, 0.5408850216893111, 0.5190457660525862, 0.49713760840920945, 0.4752254031360049, 0.4533744656929005, 0.43164998401902366, 0.4101164214705023, 0.3888369208074946, 0.36787271899666724, 0.34728258261887, 0.3271222734499283, 0.30744405331349145, 0.28829623659542597, 0.26972279787490555, 0.2517630409920839, 0.2344513345677366, 0.21781691755482313, 0.2018837768790397, 0.18667059766189847, 0.17219078496413692, 0.15845255448710202, 0.14545908827055729, 0.13320875016810133, 0.12169535480100475, 0.11090848281544048, 0.10083383461611592, 0.09145361433175414, 0.08274693558633812, 0.07469024069755677, 0.06725772518564865, 0.060421759930223286, 0.054153303932507744, 0.048422301394722776, 0.0431980576833661, 0.038449589664666724, 0.034145946854542274, 0.0302565007801706, 0.026751200876932904, 0.023600796118042038, 0.020777022374195733, 0.018252756211446522, 0.016002136446407085, 0.014000655282882938, 0.012225221251394195, 0.010654196465028541, 0.009267410897150073, 0.008046156486762187, 0.00697316389579851, 0.006032564690561829, 0.0052098416088514335, 0.004491769417010894, 0.0038663486688144872, 0.003322734461672416, 0.0028511620549328143, 0.0024428709787506155, 0.002090029027424354, 0.0017856573042805614, 0.0015235572708080104, 0.001298240554271166, 0.001104862087804041, 0.0009391569963475799, 0.000797381501252183, 0.0006762579957286263, 0.0005729243418569506, 0.00048488735639869217, 0.0004099803857607644, 0.00034632481850304286, 0.00029229534504994666, 0.0002464887470267302, 0.00020769598120373757, 0.00017487731379413836, 0.0001471402583278278, 0.0001237200731699464, 0.00010396258176888504, 8.730908885950263e-05, 7.328317821506653e-05]; + + function drawLine(ns, color, lwidth, _phase, main) { + context.strokeStyle = color; + context.lineWidth = lwidth || 1; + + let x, y; + if (main) { + context.beginPath(); + context.moveTo(0, height*0.6); + for (let i=0; i<=0.54; i+=0.06) { + x = halfWidth*i; + y = rHeight; + context.transform(1, (i-2)*0.0454, 0, 1, 0, 0); + context.lineTo(x, y); + context.resetTransform(); + } + context.stroke(); + } + + context.beginPath(); + context.transform(1, -0.066284, 0, 1, 0, 0); + context.moveTo(width*0.27, rHeight); + context.resetTransform(); + for (let i=0; i<=249; i+=1) { + x = halfWidth*(0.54+i/250); + // y = rHeight + (this.noise + ns) * _globalAttenuationArray[i] * Math.sin((-24.84+i*4.5*12/n)-phase); + y = rHeight + (this_noise + ns) * _globalAttenuationArray[i] * Math.sin((-24.84+i*0.216)-_phase); + // ctx.transform(1, -0.066284+i*0.0454/n, 0, 1, 0, 0); + context.transform(1, -0.066284+i*0.0001816, 0, 1, 0, 0); + context.lineTo(x, y); + context.resetTransform(); + } + + if (main) { + for (let i=1.46; i<=2.06; i+=0.06) { + x = halfWidth*i; + y = rHeight; + context.transform(1, (i-2)*0.0454, 0, 1, 0, 0); + context.lineTo(x, y); + context.resetTransform(); + } + } + context.stroke(); + } + + function draw() { + let trebleAm = 0; + let altoAm = 0; + let bassAm = 0; + + for(let i=0; i<6.4; i++) { + bassAm += audioData[i]; + } + for(let i=7; i<19.2; i++) { + altoAm += audioData[i]; + } + for(let i=20; i<64; i++) { + trebleAm += audioData[i]; + } + + trebleAm = trebleAm / 38.4; + altoAm = altoAm / 19.2; + bassAm = bassAm / 6.4; + + this_phase = (this_phase+this_speed)%(Math.PI*64); + + context.clearRect(0, 0, width, height); + + drawLine(this_delta*trebleAmRatio*trebleAm, trebleColor, 1.5, this_phase, false); + drawLine(this_delta*bassAmRatio*bassAm, bassColor, 1.5, this_phase+0.8, false); + drawLine(this_delta*altoAmRatio*altoAm, altoColor, 2, this_phase+0.4, true); + context.beginPath(); + context.stroke(); + } + + onAudioDataUpdeted: { + if(autoNormalizing) { + for (let i = 0; i < 128; i++) { + audioData[i] = data[i] / data[128]; + } + } else { + for (let i = 0; i < 128; i++) { + audioData[i] = data[i] * amplitude; + } + } + draw(); + requestPaint(); + } + + Component.onCompleted: { + for (let i = 0; i < 128; i++) { + audioData[i] = 0; + } + } +} diff --git a/styles/Preset_circle/Config.qml b/styles/Preset_circle/Config.qml new file mode 100644 index 0000000..11c0586 --- /dev/null +++ b/styles/Preset_circle/Config.qml @@ -0,0 +1,139 @@ +import QtQuick 2.12 +import NERvGear.Preferences 1.0 as P + +import "../../qml/api" //导入CfgAPI.qml + +CfgAPI { + version: "1.0.0" + cfg_height: 740 + + P.ColorPreference { + name: "Main Color" + label: qsTr("Spectrum Line Color") + defaultValue: "#FF4500" + } + + P.SelectPreference { + name: "Line Position" + label: qsTr("Spectrum Line Position") + defaultValue: 0 + model: [qsTr("Both"), qsTr("Outside"), qsTr("Inside")] + } + + P.SliderPreference { + name: "Line Width" + label: qsTr("Spectrum Line Width") + from: 0.1 + to: 10 + stepSize: 0.1 + defaultValue: 1 + displayValue: value.toFixed(1) + "px" + } + + P.SliderPreference { + name: "Max Range" + label: qsTr("Max Amplitude") + from: 0 + to: 100 + stepSize: 1 + defaultValue: 50 + displayValue: value + "%" + } + + P.SelectPreference { + name: "Data Length" + label: qsTr("Spectrum Length") + defaultValue: 0 + model: [64, 32, 16, 8] + } + + P.Separator {} + + P.SpinPreference { + name: "Channel" + label: qsTr("Channel") + message: "1 to 2" + display: P.TextFieldPreference.ExpandLabel + editable: false + from: 1 + to: 2 + defaultValue: 2 + } + + P.Separator {} + + P.SwitchPreference { + name: "Reverse" + label: qsTr("Reverse Spectrum") + defaultValue: false + } + + P.Separator {} + + P.SwitchPreference { + id: _cfg_preset_line_rotate + name: "Rotate" + label: qsTr("Auto Rotate") + defaultValue: false + } + + P.SliderPreference { + name: "Ratate Speed" + label: qsTr("Ratate Speed") + enabled: _cfg_preset_line_rotate.value + from: 1 + to: 100 + stepSize: 1 + defaultValue: 10 + displayValue: value + "%" + } + + P.SpinPreference { + name: "Angle" + label: qsTr("Initial Angle") + message: "0 to 359" + enabled: !_cfg_preset_line_rotate.value + display: P.TextFieldPreference.ExpandLabel + editable: true + from: 0 + to: 359 + defaultValue: 0 + } + + P.Separator {} + + P.DialogPreference { + name: "Data Settings" + label: qsTr("Data Settings") + live: true + icon.name: "regular:\uf1de" + + P.SwitchPreference { + id: _cfg_preset_circle_dataSettings_autoNormalizing + name: "Auto Normalizing" + label: qsTr("Auto Normalizing") + defaultValue: true + } + + P.SpinPreference { + name: "Amplitude" + label: qsTr("Amplitude Ratio") + enabled: !_cfg_preset_circle_dataSettings_autoNormalizing.value + message: "1 to 100" + display: P.TextFieldPreference.ExpandLabel + editable: true + from: 1 + to: 100 + defaultValue: 10 + } + + P.Separator {} + + P.SelectPreference { + name: "Unit Style" + label: qsTr("Display Style") + defaultValue: 0 + model: [qsTr("Linear"), qsTr("Decibel")] + } + } +} diff --git a/styles/Preset_circle/Style.qml b/styles/Preset_circle/Style.qml new file mode 100644 index 0000000..af6dcc5 --- /dev/null +++ b/styles/Preset_circle/Style.qml @@ -0,0 +1,132 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 + +import "../../qml/api" + +StyleAPI { + readonly property var audioData: new Array(128) + + //configs + readonly property string color: configs["Main Color"] + readonly property int linePosition: configs["Line Position"] + readonly property real lineWidth: configs["Line Width"] + readonly property real maxRange: configs["Max Range"] / 100 + readonly property int uDataLen: Math.pow(2, configs["Data Length"]) + readonly property int dataLength: 64/uDataLen + readonly property int channel: configs["Channel"] + readonly property bool reverse: configs["Reverse"] + readonly property bool rotateFlag: configs["Rotate"] + readonly property real rSpeed: configs["Ratate Speed"] / 100 + readonly property real angle: configs["Angle"] + readonly property bool autoNormalizing: configs["Data Settings"]["Auto Normalizing"] + readonly property real amplitude: configs["Data Settings"]["Amplitude"] / 400 + readonly property int unitStyle: configs["Data Settings"]["Unit Style"] + + readonly property int total: channel*dataLength + + readonly property real dotGap: 360/total + property real offsetAngle: 0 + property var outerPos: [] + property var innerPos: [] + readonly property real degUnit: Math.PI/180 + + readonly property real subRatio: 0.2*maxRange + readonly property real mainRatio: 1-subRatio*2.5 + + readonly property real minLength: Math.min(width, height) + readonly property real ratio:minLength*subRatio + readonly property real halfWidth: width/2 + readonly property real halfHeight: height/2 + readonly property real halfMinLength: minLength/2 + readonly property real logAmplitude: Math.log10(amplitude) + + + onConfigsUpdated: { + context.lineWidth = lineWidth; + context.strokeStyle = color; + } + + function getPos(r, deg) { + return [halfWidth+Math.cos(deg)*r,halfHeight+Math.sin(deg)*r]; + } + + function createPoint() { + outerPos = []; + innerPos = []; + let deg, deltaR, r1, r2, _rhmLen; + _rhmLen = mainRatio*halfMinLength; + + for (let j=0; j < channel; j++) { + for (let i=0; i < dataLength; i++) { + deg = degUnit*((i+j*dataLength)*dotGap + offsetAngle); + deltaR = audioData[reverse*(dataLength-i-1)+(!reverse)*(i+j*dataLength)] * ratio; + r1 = _rhmLen+1+deltaR*(linePosition!==2); + r2 = _rhmLen-1-deltaR*(linePosition!==1); + outerPos.push(getPos(r1, deg)); + innerPos.push(getPos(r2, deg)); + } + } + offsetAngle = rotateFlag ? ((offsetAngle + rSpeed) % 360) : angle; + } + + onAudioDataUpdeted: { + if(autoNormalizing) { + if (unitStyle) { + //对数化显示 + let logPeak = Math.log10(data[128]); + for(let i=0; i