SAO Utils 2的音频可视化插件
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

241 lines
7.4 KiB

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 defaultServerCFG: {
"server": {
"port": 5050,
"maxclient": 5,
"logger": true
},
"fft": {
"attack": 5,
"decay": 5,
"norspeed": 1,
"peakthr": 10,
"fps": 35,
"changespeed": 20
}
}
readonly property string iniFile: "../bin/advConfig.ini"
readonly property string serverEXE: "../bin/ADVServer.exe"
property var serverCFG: defaultServerCFG
property string wsIp: "localhost"
property int wsPort: serverCFG.server.port
property int widgetsNum: 0
property bool debug: false
property bool rebootFlag: false
signal audioDataUpdated(var audioData)
signal serverPreferencesOpen()
function execute(path, args) {
path = NVG.Url.toLocalFile(Qt.resolvedUrl(path));
NVG.SystemCall.execute(path, args);
}
function readFile(fileUrl) {
let request = new XMLHttpRequest();
request.open("GET", fileUrl, false);
request.send(null);
let data = request.responseText;
request = null;
return data;
}
function writeFile(fileUrl, text) {
var request = new XMLHttpRequest();
request.open("PUT", fileUrl, false);
request.send(text);
return request.status;
}
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 (["true", "false"].indexOf(match[2]) > -1) {
match[2] = Boolean(1 - ["true", "false"].indexOf(match[2]));
} else if (/^\d+$/.test(match[2])) {
match[2] = Number(match[2]);
}
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;
}
function convertINIString(data) {
let value = "";
for (let section in data) {
value += "[" + section + "]\n";
for (let key in data[section]) {
value += key + " = " + String(data[section][key]) + "\n";
}
value += "\n";
}
return value;
}
function deepClone(obj) {
let objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj === "object" && obj != null) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone(obj[key]);
} else {
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
//支持二级嵌套对象的更新操作
function updateObject(targetObj, sourceObj) {
for (let prop in sourceObj) {
if (sourceObj.hasOwnProperty(prop) && sourceObj[prop] !== undefined) {
if (typeof sourceObj[prop] === 'object') {
if (targetObj[prop] === undefined)
targetObj[prop] = {};
Object.assign(targetObj[prop], sourceObj[prop]);
} else {
targetObj[prop] = sourceObj[prop];
}
}
}
return targetObj;
}
function isObjectValueEqual(a, b) {
if (a === b)
return true;
let aProps = Object.getOwnPropertyNames(a);
let bProps = Object.getOwnPropertyNames(b);
if (aProps.length !== bProps.length)
return false;
for (let prop in a) {
if (b.hasOwnProperty(prop)) {
if (typeof a[prop] === 'object') {
if (!isObjectValueEqual(a[prop], b[prop]))
return false;
} else if (a[prop] !== b[prop]) {
return false;
}
} else {
return false;
}
}
return true;
}
Loader {
id: wsocket
sourceComponent: WSocket {}
active: false
}
onWidgetsNumChanged: {
wsocket.active = widgetsNum>0;
}
function setWsocket(status) {
wsocket.active = status;
}
function rebootServer(force) {
if (force || !debug && wsocket.active && rebootFlag) {
console.log("Try to reboot ADVServer...");
execute(serverEXE, "-reboot");
if (widgetsNum) {
wsocket.active = false;
wsocket.active = true;
}
rebootFlag = false;
}
}
onDebugChanged: {
rebootServer();
}
onRebootFlagChanged: {
rebootServer();
if (rebootFlag && debug) {
NVG.SystemCall.execute("explorer", NVG.Url.toLocalFile(Qt.resolvedUrl("../bin/ADV_Log.log")).replace(/\//g, '\\'));
}
}
function parse_resource(resource_list, preset) {
if (preset)
resource_list.sort(function (x, y) {
let preset_order = ["/advp-style-preset/line", "/advp-style-preset/gradient_line", "/advp-style-preset/waves", "/advp-style-preset/circle", "/advp-style-preset/solidcircle", "/advp-style-preset/ordinal_scale_ui_bottom"];
if (preset_order.indexOf(x.location) < preset_order.indexOf(y.location))
return -1;
else if(preset_order.indexOf(x.location) > preset_order.indexOf(y.location))
return 1;
else
return 0;
});
resource_list.forEach(function (resource) {
if (resource.url && stylesURL.indexOf(resource.url.toString()) === -1) {
styles.push(resource.title);
stylesURL.push(resource.url.toString());
}
});
}
function updateStyleList() {
styles.length = 0;
stylesURL.length = 0;
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);
}
Component.onCompleted: {
updateStyleList();
let ini_data = readFile(iniFile);
if (ini_data) {
ini_data = ini_data.toLowerCase();
let cfg = parseINIString(ini_data);
serverCFG = Object.assign(deepClone(defaultServerCFG), cfg);
} else {
let ini_text = convertINIString(defaultServerCFG);
writeFile(iniFile, ini_text);
}
}
}