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 @@
+
+
\ 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 @@
+
+
\ 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 @@
+
+
\ 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 @@
+
+
\ 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 @@
+
+
\ 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
+
+
+
+---
+
+# 简介
+
+`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