diff --git a/build-config/renderer-lyric/webpack.config.base.js b/build-config/renderer-lyric/webpack.config.base.js index 5fada8d1..2e668c10 100644 --- a/build-config/renderer-lyric/webpack.config.base.js +++ b/build-config/renderer-lyric/webpack.config.base.js @@ -20,7 +20,7 @@ module.exports = { type: 'commonjs2', }, path: path.join(__dirname, '../../dist'), - publicPath: 'auto', + publicPath: '', }, resolve: { alias: { diff --git a/build-config/renderer-scripts/webpack.config.base.js b/build-config/renderer-scripts/webpack.config.base.js index ea04bf2c..d1c3dac6 100644 --- a/build-config/renderer-scripts/webpack.config.base.js +++ b/build-config/renderer-scripts/webpack.config.base.js @@ -12,7 +12,7 @@ module.exports = { type: 'commonjs2', }, path: path.join(__dirname, '../../dist'), - publicPath: 'auto', + publicPath: '', }, resolve: { alias: { diff --git a/build-config/renderer/webpack.config.base.js b/build-config/renderer/webpack.config.base.js index 2fab76ae..26b9a2bc 100644 --- a/build-config/renderer/webpack.config.base.js +++ b/build-config/renderer/webpack.config.base.js @@ -20,7 +20,7 @@ module.exports = { type: 'commonjs2', }, path: path.join(__dirname, '../../dist'), - publicPath: 'auto', + publicPath: '', }, resolve: { alias: { @@ -39,14 +39,6 @@ module.exports = { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/, - // parser: { - // worker: [ - // '*context.audioWorklet.addModule()', - // '*audioWorklet.addModule()', - // // *addModule() is not valid syntax - // // '...', - // ], - // }, }, { test: /\.tsx?$/, @@ -57,6 +49,12 @@ module.exports = { appendTsSuffixTo: [/\.vue$/], }, }, + parser: { + worker: [ + '*audioContext.audioWorklet.addModule()', + '...', + ], + }, }, { test: /\.node$/, diff --git a/package-lock.json b/package-lock.json index 212be39b..4653a83d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "lx-music-desktop", - "version": "2.3.0-beta.5", + "version": "2.3.0-beta.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "lx-music-desktop", - "version": "2.3.0-beta.5", + "version": "2.3.0-beta.6", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -58,11 +58,11 @@ "css-loader": "^6.8.1", "css-minimizer-webpack-plugin": "^5.0.0", "del": "^6.1.1", - "electron": "^22.3.11", + "electron": "^22.3.12", "electron-builder": "^24.4.0", "electron-debug": "^3.2.0", "electron-devtools-installer": "^3.2.0", - "electron-to-chromium": "^1.4.413", + "electron-to-chromium": "^1.4.416", "electron-updater": "^6.1.0", "eslint": "^8.41.0", "eslint-config-standard": "^17.1.0", @@ -77,7 +77,7 @@ "eslint-webpack-plugin": "^4.0.1", "html-webpack-plugin": "^5.5.1", "less": "^4.1.3", - "less-loader": "^11.1.1", + "less-loader": "^11.1.2", "mini-css-extract-plugin": "^2.7.6", "node-loader": "^2.0.0", "postcss": "^8.4.24", @@ -90,14 +90,14 @@ "svg-sprite-loader": "^6.0.11", "svg-transform-loader": "^2.0.13", "svgo-loader": "^4.0.0", - "terser": "^5.17.6", + "terser": "^5.17.7", "terser-webpack-plugin": "^5.3.9", "ts-loader": "^9.4.3", "typescript": "^5.0.4", "vue-eslint-parser": "^9.3.0", - "vue-loader": "^17.1.2", + "vue-loader": "^17.2.1", "vue-template-compiler": "^2.7.14", - "webpack": "^5.84.1", + "webpack": "^5.85.0", "webpack-cli": "^5.1.1", "webpack-dev-server": "^4.15.0", "webpack-hot-middleware": "github:lyswhut/webpack-hot-middleware#329c4375134b89d39da23a56a94db651247c74a1", @@ -2647,9 +2647,9 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", + "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", @@ -2657,9 +2657,9 @@ } }, "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "dependencies": { "@jridgewell/set-array": "^1.0.1", @@ -4038,9 +4038,9 @@ } }, "node_modules/acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -4679,12 +4679,6 @@ "node": ">=0.12.0" } }, - "node_modules/async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -6756,21 +6750,6 @@ "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", "dev": true }, - "node_modules/deasync": { - "version": "0.1.28", - "resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.28.tgz", - "integrity": "sha512-QqLF6inIDwiATrfROIyQtwOQxjZuek13WRYZ7donU5wJPLoP67MnYxA6QtqdvdBy2mMqv5m3UefBVdJjvevOYg==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "dependencies": { - "bindings": "^1.5.0", - "node-addon-api": "^1.7.1" - }, - "engines": { - "node": ">=0.11.0" - } - }, "node_modules/debounce-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", @@ -7305,9 +7284,9 @@ } }, "node_modules/electron": { - "version": "22.3.11", - "resolved": "https://registry.npmjs.org/electron/-/electron-22.3.11.tgz", - "integrity": "sha512-4PW1rJRUckJUCxTXRJkzJ7qlGTZ8Qfwoke5aFlaGccmn/zViuE9iSCg9zqIx00rzsbF9R5j8j9V4tAqyqjjJRA==", + "version": "22.3.12", + "resolved": "https://registry.npmjs.org/electron/-/electron-22.3.12.tgz", + "integrity": "sha512-CVjR9dp4UTm4TlwoeNnkmdnCye96giyw9Ie2VLlMUnOTJur2588zaTHpdRuE3006/gdMM9lPx2jC7uBFdadmKg==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -7548,9 +7527,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.413", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.413.tgz", - "integrity": "sha512-Gd+/OAhRca06dkVxIQo/W7dr6Nmk9cx6lQdZ19GvFp51k5B/lUAokm6SJfNkdV8kFLsC3Z4sLTyEHWCnB1Efbw==", + "version": "1.4.416", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.416.tgz", + "integrity": "sha512-AUYh0XDTb2vrj0rj82jb3P9hHSyzQNdTPYWZIhPdCOui7/vpme7+HTE07BE5jwuqg/34TZ8ktlRz6GImJ4IXjA==", "dev": true }, "node_modules/electron-updater": { @@ -11104,13 +11083,12 @@ } }, "node_modules/less-loader": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.1.1.tgz", - "integrity": "sha512-sxfYqC6nYpZMF57gMmtlgbNwtH++zOCqqRnOlrvmgtNTQEyX7jFb1GlMXOpHb1+B8KZugOK72onl1SEkEgWH2g==", + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.1.2.tgz", + "integrity": "sha512-2bSaN2j13bUh/5BuwJKuY2DDWVmfBsS6oWRe8v1mGj7F0EpcL+WyWkDQVoEfsVRE4ac5/OuP44ZCaVsXWrQQ9A==", "dev": true, "dependencies": { - "klona": "^2.0.6", - "v": "^0.3.0" + "klona": "^2.0.6" }, "engines": { "node": ">= 14.15.0" @@ -15585,46 +15563,6 @@ "semver": "bin/semver.js" } }, - "node_modules/simple-websocket": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/simple-websocket/-/simple-websocket-5.1.1.tgz", - "integrity": "sha512-1yCq3y3XtzENKnCT5ycsV2f45PlQoIQOKeSlOMQ43Z2rKkIxLsJz3dZRRccHJE/rdbMozLUl/SRisdLchX2TrQ==", - "dev": true, - "dependencies": { - "debug": "^3.1.0", - "inherits": "^2.0.1", - "randombytes": "^2.0.3", - "readable-stream": "^2.0.5", - "safe-buffer": "^5.0.1", - "ws": "^3.3.1" - } - }, - "node_modules/simple-websocket/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/simple-websocket/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/simple-websocket/node_modules/ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "dev": true, - "dependencies": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -16922,13 +16860,13 @@ } }, "node_modules/terser": { - "version": "5.17.6", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.6.tgz", - "integrity": "sha512-V8QHcs8YuyLkLHsJO5ucyff1ykrLVsR4dNnS//L5Y3NiSXpbK1J+WMVUs67eI0KTxs9JtHhgEQpXQVHlHI92DQ==", + "version": "5.17.7", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.7.tgz", + "integrity": "sha512-/bi0Zm2C6VAexlGgLlVxA0P2lru/sdLyfCVaRMfKVo9nWxbmz7f/sD8VPybPeSUJaJcwmCJis9pBIhcVcG1QcQ==", "dev": true, "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -17402,12 +17340,6 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", - "dev": true - }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -17734,37 +17666,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/v": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/v/-/v-0.3.0.tgz", - "integrity": "sha512-9grXbMw2B4ScMvE9iL2tre9Qfy46zWDNjSptgFCraHfgqRQMhPlWkFHKvUOKjBFvOAxeRTiaY6SySPLXFBt2oQ==", - "dev": true, - "dependencies": { - "debug": "^2.6.1", - "simple-websocket": "^5.0.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "optionalDependencies": { - "deasync": "^0.1.9" - } - }, - "node_modules/v/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/v/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -17980,9 +17881,9 @@ "dev": true }, "node_modules/vue-loader": { - "version": "17.1.2", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.1.2.tgz", - "integrity": "sha512-AxCiJ2aPPwJC5CWtsQH3d5WSw67v55oLFHBmzrc5kjSr6i1dn5uoj5QsuokApirvWBCegRmfnE/MYFyhKMUwxw==", + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.2.1.tgz", + "integrity": "sha512-6vIZPxekZ23gWQtVlaJgT1sKW9zV7YeiW1kgtdQ0rpbMWmfoajeZ+GS17xtXDlz8ZpydrFKPza1CrwHSG4lIww==", "dev": true, "dependencies": { "chalk": "^4.1.0", @@ -18057,9 +17958,9 @@ } }, "node_modules/webpack": { - "version": "5.84.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.84.1.tgz", - "integrity": "sha512-ZP4qaZ7vVn/K8WN/p990SGATmrL1qg4heP/MrVneczYtpDGJWlrgZv55vxaV2ul885Kz+25MP2kSXkPe3LZfmg==", + "version": "5.85.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.85.0.tgz", + "integrity": "sha512-7gazTiYqwo5OSqwH1tigLDL2r3qDeP2dOKYgd+LlXpsUMqDTklg6tOghexqky0/+6QY38kb/R/uRPUleuL43zg==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -20361,9 +20262,9 @@ "dev": true }, "@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", + "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", "dev": true, "requires": { "@jridgewell/gen-mapping": "^0.3.0", @@ -20371,9 +20272,9 @@ }, "dependencies": { "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "requires": { "@jridgewell/set-array": "^1.0.1", @@ -21523,9 +21424,9 @@ } }, "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", "dev": true }, "acorn-import-assertions": { @@ -22022,12 +21923,6 @@ "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", "dev": true }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true - }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -23578,17 +23473,6 @@ "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", "dev": true }, - "deasync": { - "version": "0.1.28", - "resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.28.tgz", - "integrity": "sha512-QqLF6inIDwiATrfROIyQtwOQxjZuek13WRYZ7donU5wJPLoP67MnYxA6QtqdvdBy2mMqv5m3UefBVdJjvevOYg==", - "dev": true, - "optional": true, - "requires": { - "bindings": "^1.5.0", - "node-addon-api": "^1.7.1" - } - }, "debounce-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", @@ -23985,9 +23869,9 @@ } }, "electron": { - "version": "22.3.11", - "resolved": "https://registry.npmjs.org/electron/-/electron-22.3.11.tgz", - "integrity": "sha512-4PW1rJRUckJUCxTXRJkzJ7qlGTZ8Qfwoke5aFlaGccmn/zViuE9iSCg9zqIx00rzsbF9R5j8j9V4tAqyqjjJRA==", + "version": "22.3.12", + "resolved": "https://registry.npmjs.org/electron/-/electron-22.3.12.tgz", + "integrity": "sha512-CVjR9dp4UTm4TlwoeNnkmdnCye96giyw9Ie2VLlMUnOTJur2588zaTHpdRuE3006/gdMM9lPx2jC7uBFdadmKg==", "dev": true, "requires": { "@electron/get": "^2.0.0", @@ -24191,9 +24075,9 @@ } }, "electron-to-chromium": { - "version": "1.4.413", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.413.tgz", - "integrity": "sha512-Gd+/OAhRca06dkVxIQo/W7dr6Nmk9cx6lQdZ19GvFp51k5B/lUAokm6SJfNkdV8kFLsC3Z4sLTyEHWCnB1Efbw==", + "version": "1.4.416", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.416.tgz", + "integrity": "sha512-AUYh0XDTb2vrj0rj82jb3P9hHSyzQNdTPYWZIhPdCOui7/vpme7+HTE07BE5jwuqg/34TZ8ktlRz6GImJ4IXjA==", "dev": true }, "electron-updater": { @@ -26887,13 +26771,12 @@ } }, "less-loader": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.1.1.tgz", - "integrity": "sha512-sxfYqC6nYpZMF57gMmtlgbNwtH++zOCqqRnOlrvmgtNTQEyX7jFb1GlMXOpHb1+B8KZugOK72onl1SEkEgWH2g==", + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.1.2.tgz", + "integrity": "sha512-2bSaN2j13bUh/5BuwJKuY2DDWVmfBsS6oWRe8v1mGj7F0EpcL+WyWkDQVoEfsVRE4ac5/OuP44ZCaVsXWrQQ9A==", "dev": true, "requires": { - "klona": "^2.0.6", - "v": "^0.3.0" + "klona": "^2.0.6" } }, "levn": { @@ -30253,48 +30136,6 @@ } } }, - "simple-websocket": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/simple-websocket/-/simple-websocket-5.1.1.tgz", - "integrity": "sha512-1yCq3y3XtzENKnCT5ycsV2f45PlQoIQOKeSlOMQ43Z2rKkIxLsJz3dZRRccHJE/rdbMozLUl/SRisdLchX2TrQ==", - "dev": true, - "requires": { - "debug": "^3.1.0", - "inherits": "^2.0.1", - "randombytes": "^2.0.3", - "readable-stream": "^2.0.5", - "safe-buffer": "^5.0.1", - "ws": "^3.3.1" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } - } - } - }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -31335,13 +31176,13 @@ } }, "terser": { - "version": "5.17.6", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.6.tgz", - "integrity": "sha512-V8QHcs8YuyLkLHsJO5ucyff1ykrLVsR4dNnS//L5Y3NiSXpbK1J+WMVUs67eI0KTxs9JtHhgEQpXQVHlHI92DQ==", + "version": "5.17.7", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.7.tgz", + "integrity": "sha512-/bi0Zm2C6VAexlGgLlVxA0P2lru/sdLyfCVaRMfKVo9nWxbmz7f/sD8VPybPeSUJaJcwmCJis9pBIhcVcG1QcQ==", "dev": true, "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -31692,12 +31533,6 @@ } } }, - "ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", - "dev": true - }, "unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -31945,34 +31780,6 @@ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true }, - "v": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/v/-/v-0.3.0.tgz", - "integrity": "sha512-9grXbMw2B4ScMvE9iL2tre9Qfy46zWDNjSptgFCraHfgqRQMhPlWkFHKvUOKjBFvOAxeRTiaY6SySPLXFBt2oQ==", - "dev": true, - "requires": { - "deasync": "^0.1.9", - "debug": "^2.6.1", - "simple-websocket": "^5.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -32150,9 +31957,9 @@ } }, "vue-loader": { - "version": "17.1.2", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.1.2.tgz", - "integrity": "sha512-AxCiJ2aPPwJC5CWtsQH3d5WSw67v55oLFHBmzrc5kjSr6i1dn5uoj5QsuokApirvWBCegRmfnE/MYFyhKMUwxw==", + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.2.1.tgz", + "integrity": "sha512-6vIZPxekZ23gWQtVlaJgT1sKW9zV7YeiW1kgtdQ0rpbMWmfoajeZ+GS17xtXDlz8ZpydrFKPza1CrwHSG4lIww==", "dev": true, "requires": { "chalk": "^4.1.0", @@ -32207,9 +32014,9 @@ } }, "webpack": { - "version": "5.84.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.84.1.tgz", - "integrity": "sha512-ZP4qaZ7vVn/K8WN/p990SGATmrL1qg4heP/MrVneczYtpDGJWlrgZv55vxaV2ul885Kz+25MP2kSXkPe3LZfmg==", + "version": "5.85.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.85.0.tgz", + "integrity": "sha512-7gazTiYqwo5OSqwH1tigLDL2r3qDeP2dOKYgd+LlXpsUMqDTklg6tOghexqky0/+6QY38kb/R/uRPUleuL43zg==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.3", diff --git a/package.json b/package.json index 87e399fe..c6ba27ea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lx-music-desktop", - "version": "2.3.0-beta.5", + "version": "2.3.0-beta.6", "description": "一个免费的音乐查找助手", "main": "./dist/main.js", "productName": "lx-music-desktop", @@ -230,11 +230,11 @@ "css-loader": "^6.8.1", "css-minimizer-webpack-plugin": "^5.0.0", "del": "^6.1.1", - "electron": "^22.3.11", + "electron": "^22.3.12", "electron-builder": "^24.4.0", "electron-debug": "^3.2.0", "electron-devtools-installer": "^3.2.0", - "electron-to-chromium": "^1.4.413", + "electron-to-chromium": "^1.4.416", "electron-updater": "^6.1.0", "eslint": "^8.41.0", "eslint-config-standard": "^17.1.0", @@ -249,7 +249,7 @@ "eslint-webpack-plugin": "^4.0.1", "html-webpack-plugin": "^5.5.1", "less": "^4.1.3", - "less-loader": "^11.1.1", + "less-loader": "^11.1.2", "mini-css-extract-plugin": "^2.7.6", "node-loader": "^2.0.0", "postcss": "^8.4.24", @@ -262,14 +262,14 @@ "svg-sprite-loader": "^6.0.11", "svg-transform-loader": "^2.0.13", "svgo-loader": "^4.0.0", - "terser": "^5.17.6", + "terser": "^5.17.7", "terser-webpack-plugin": "^5.3.9", "ts-loader": "^9.4.3", "typescript": "^5.0.4", "vue-eslint-parser": "^9.3.0", - "vue-loader": "^17.1.2", + "vue-loader": "^17.2.1", "vue-template-compiler": "^2.7.14", - "webpack": "^5.84.1", + "webpack": "^5.85.0", "webpack-cli": "^5.1.1", "webpack-dev-server": "^4.15.0", "webpack-hot-middleware": "github:lyswhut/webpack-hot-middleware#329c4375134b89d39da23a56a94db651247c74a1", diff --git a/publish/changeLog.md b/publish/changeLog.md index bf1185e1..89d154bb 100644 --- a/publish/changeLog.md +++ b/publish/changeLog.md @@ -13,4 +13,4 @@ ### 其他 -- 更新 electron 到 v22.3.11 +- 更新 electron 到 v22.3.12 diff --git a/src/renderer/plugins/player.ts b/src/renderer/plugins/player/index.ts similarity index 97% rename from src/renderer/plugins/player.ts rename to src/renderer/plugins/player/index.ts index c058de1b..1c5f7950 100644 --- a/src/renderer/plugins/player.ts +++ b/src/renderer/plugins/player/index.ts @@ -39,7 +39,7 @@ export const convolutions = [ // { name: 'tim_omni_rear_blend', mainGain: 1.8, sendGain: 0.8, source: 'tim-omni-rear-blend.wav' }, ] as const // 半音 -export const semitones = [-1.5, -1, -0.5, 0.5, 1, 1.5, 2, 2.5, 3, 3.5] as const +// export const semitones = [-1.5, -1, -0.5, 0.5, 1, 1.5, 2, 2.5, 3, 3.5] as const let convolver: ConvolverNode let convolverSourceGainNode: GainNode @@ -48,7 +48,7 @@ let convolverDynamicsCompressor: DynamicsCompressorNode let gainNode: GainNode let panner: PannerNode let pitchShifterNode: AudioWorkletNode -let pitchShifterNodeAudioParam: AudioParam +let pitchShifterNodePitchFactor: AudioParam let pitchShifterNodeLoadStatus: 'none' | 'loading' | 'unconnect' | 'connected' = 'none' let pitchShifterNodeTempValue = 1 export const soundR = 0.5 @@ -225,14 +225,14 @@ const loadPitchShifterNode = () => { // source -> analyser -> biquadFilter -> audioWorklet(pitch shifter) -> [(convolver & convolverSource)->convolverDynamicsCompressor] -> panner -> gain void audioContext.audioWorklet.addModule(new URL( /* webpackChunkName: 'pitch_shifter.audioWorklet' */ - '@renderer/utils/pitch-shifter/phase-vocoder.js', + './pitch-shifter/phase-vocoder.js', import.meta.url, )).then(() => { console.log('pitch shifter audio worklet loaded') pitchShifterNode = new AudioWorkletNode(audioContext, 'phase-vocoder-processor') - let audioParam = pitchShifterNode.parameters.get('pitchFactor') - if (!audioParam) return - pitchShifterNodeAudioParam = audioParam + let pitchFactorParam = pitchShifterNode.parameters.get('pitchFactor') + if (!pitchFactorParam) return + pitchShifterNodePitchFactor = pitchFactorParam pitchShifterNodeLoadStatus = 'unconnect' if (pitchShifterNodeTempValue == 1) return @@ -250,7 +250,7 @@ const connectPitchShifterNode = () => { // convolverDynamicsCompressor.connect(pitchShifterNode) // pitchShifterNode.connect(panner) pitchShifterNodeLoadStatus = 'connected' - pitchShifterNodeAudioParam.value = pitchShifterNodeTempValue + pitchShifterNodePitchFactor.value = pitchShifterNodeTempValue } // const disconnectPitchShifterNode = () => { // const lastBiquadFilter = (biquads.get(`hz${freqs.at(-1) as Freqs}`) as BiquadFilterNode) @@ -275,7 +275,7 @@ export const setPitchShifter = (val: number) => { case 'connected': // a: 1 = 半音 // value = 2 ** (a / 12) - pitchShifterNodeAudioParam.value = val + pitchShifterNodePitchFactor.value = val break case 'unconnect': connectPitchShifterNode() diff --git a/src/renderer/utils/pitch-shifter/phase-vocoder.js b/src/renderer/plugins/player/pitch-shifter/fft.js similarity index 51% rename from src/renderer/utils/pitch-shifter/phase-vocoder.js rename to src/renderer/plugins/player/pitch-shifter/fft.js index ceaac743..db3be21b 100644 --- a/src/renderer/utils/pitch-shifter/phase-vocoder.js +++ b/src/renderer/plugins/player/pitch-shifter/fft.js @@ -1,6 +1,5 @@ -// https://github.com/olvb/phaze // https://github.com/indutny/fft.js -// import OLAProcessor from './ola-processor' + function FFT(size) { this.size = size | 0 @@ -498,353 +497,4 @@ FFT.prototype._singleRealTransform4 = function _singleRealTransform4(outOff, out[outOff + 7] = FDi } - -const WEBAUDIO_BLOCK_SIZE = 128 - -/** Overlap-Add Node */ -// eslint-disable-next-line no-undef -class OLAProcessor extends AudioWorkletProcessor { - constructor(options) { - super(options) - - this.nbInputs = options.numberOfInputs - this.nbOutputs = options.numberOfOutputs - - this.blockSize = options.processorOptions.blockSize - // TODO for now, the only support hop size is the size of a web audio block - this.hopSize = WEBAUDIO_BLOCK_SIZE - - this.nbOverlaps = this.blockSize / this.hopSize - - // pre-allocate input buffers (will be reallocated if needed) - this.inputBuffers = new Array(this.nbInputs) - this.inputBuffersHead = new Array(this.nbInputs) - this.inputBuffersToSend = new Array(this.nbInputs) - // default to 1 channel per input until we know more - for (let i = 0; i < this.nbInputs; i++) { - this.allocateInputChannels(i, 1) - } - // pre-allocate input buffers (will be reallocated if needed) - this.outputBuffers = new Array(this.nbOutputs) - this.outputBuffersToRetrieve = new Array(this.nbOutputs) - // default to 1 channel per output until we know more - for (let i = 0; i < this.nbOutputs; i++) { - this.allocateOutputChannels(i, 1) - } - } - - /** Handles dynamic reallocation of input/output channels buffer - (channel numbers may vary during lifecycle) **/ - reallocateChannelsIfNeeded(inputs, outputs) { - for (let i = 0; i < this.nbInputs; i++) { - let nbChannels = inputs[i].length - if (nbChannels != this.inputBuffers[i].length) { - this.allocateInputChannels(i, nbChannels) - } - } - - for (let i = 0; i < this.nbOutputs; i++) { - let nbChannels = outputs[i].length - if (nbChannels != this.outputBuffers[i].length) { - this.allocateOutputChannels(i, nbChannels) - } - } - } - - allocateInputChannels(inputIndex, nbChannels) { - // allocate input buffers - - this.inputBuffers[inputIndex] = new Array(nbChannels) - for (let i = 0; i < nbChannels; i++) { - this.inputBuffers[inputIndex][i] = new Float32Array(this.blockSize + WEBAUDIO_BLOCK_SIZE) - this.inputBuffers[inputIndex][i].fill(0) - } - - // allocate input buffers to send and head pointers to copy from - // (cannot directly send a pointer/subarray because input may be modified) - this.inputBuffersHead[inputIndex] = new Array(nbChannels) - this.inputBuffersToSend[inputIndex] = new Array(nbChannels) - for (let i = 0; i < nbChannels; i++) { - this.inputBuffersHead[inputIndex][i] = this.inputBuffers[inputIndex][i].subarray(0, this.blockSize) - this.inputBuffersToSend[inputIndex][i] = new Float32Array(this.blockSize) - } - } - - allocateOutputChannels(outputIndex, nbChannels) { - // allocate output buffers - this.outputBuffers[outputIndex] = new Array(nbChannels) - for (let i = 0; i < nbChannels; i++) { - this.outputBuffers[outputIndex][i] = new Float32Array(this.blockSize) - this.outputBuffers[outputIndex][i].fill(0) - } - - // allocate output buffers to retrieve - // (cannot send a pointer/subarray because new output has to be add to exising output) - this.outputBuffersToRetrieve[outputIndex] = new Array(nbChannels) - for (let i = 0; i < nbChannels; i++) { - this.outputBuffersToRetrieve[outputIndex][i] = new Float32Array(this.blockSize) - this.outputBuffersToRetrieve[outputIndex][i].fill(0) - } - } - - /** Read next web audio block to input buffers **/ - readInputs(inputs) { - // when playback is paused, we may stop receiving new samples - if (inputs[0].length && inputs[0][0].length == 0) { - for (let i = 0; i < this.nbInputs; i++) { - for (let j = 0; j < this.inputBuffers[i].length; j++) { - this.inputBuffers[i][j].fill(0, this.blockSize) - } - } - return - } - - for (let i = 0; i < this.nbInputs; i++) { - for (let j = 0; j < this.inputBuffers[i].length; j++) { - let webAudioBlock = inputs[i][j] - this.inputBuffers[i][j].set(webAudioBlock, this.blockSize) - } - } - } - - /** Write next web audio block from output buffers **/ - writeOutputs(outputs) { - for (let i = 0; i < this.nbInputs; i++) { - for (let j = 0; j < this.inputBuffers[i].length; j++) { - let webAudioBlock = this.outputBuffers[i][j].subarray(0, WEBAUDIO_BLOCK_SIZE) - outputs[i][j].set(webAudioBlock) - } - } - } - - /** Shift left content of input buffers to receive new web audio block **/ - shiftInputBuffers() { - for (let i = 0; i < this.nbInputs; i++) { - for (let j = 0; j < this.inputBuffers[i].length; j++) { - this.inputBuffers[i][j].copyWithin(0, WEBAUDIO_BLOCK_SIZE) - } - } - } - - /** Shift left content of output buffers to receive new web audio block **/ - shiftOutputBuffers() { - for (let i = 0; i < this.nbOutputs; i++) { - for (let j = 0; j < this.outputBuffers[i].length; j++) { - this.outputBuffers[i][j].copyWithin(0, WEBAUDIO_BLOCK_SIZE) - this.outputBuffers[i][j].subarray(this.blockSize - WEBAUDIO_BLOCK_SIZE).fill(0) - } - } - } - - /** Copy contents of input buffers to buffer actually sent to process **/ - prepareInputBuffersToSend() { - for (let i = 0; i < this.nbInputs; i++) { - for (let j = 0; j < this.inputBuffers[i].length; j++) { - this.inputBuffersToSend[i][j].set(this.inputBuffersHead[i][j]) - } - } - } - - /** Add contents of output buffers just processed to output buffers **/ - handleOutputBuffersToRetrieve() { - for (let i = 0; i < this.nbOutputs; i++) { - for (let j = 0; j < this.outputBuffers[i].length; j++) { - for (let k = 0; k < this.blockSize; k++) { - this.outputBuffers[i][j][k] += this.outputBuffersToRetrieve[i][j][k] / this.nbOverlaps - } - } - } - } - - process(inputs, outputs, params) { - this.reallocateChannelsIfNeeded(inputs, outputs) - - this.readInputs(inputs) - this.shiftInputBuffers() - this.prepareInputBuffersToSend() - this.processOLA(this.inputBuffersToSend, this.outputBuffersToRetrieve, params) - this.handleOutputBuffersToRetrieve() - this.writeOutputs(outputs) - this.shiftOutputBuffers() - - return true - } - - processOLA(inputs, outputs, params) { - console.assert(false, 'Not overriden') - } -} - -const BUFFERED_BLOCK_SIZE = 4096 - -function genHannWindow(length) { - let win = new Float32Array(length) - for (let i = 0; i < length; i++) { - win[i] = 0.8 * (1 - Math.cos(2 * Math.PI * i / length)) - } - return win -} - -class PhaseVocoderProcessor extends OLAProcessor { - static get parameterDescriptors() { - return [{ - name: 'pitchFactor', - defaultValue: 1.0, - }] - } - - constructor(options) { - options.processorOptions = { - blockSize: BUFFERED_BLOCK_SIZE, - } - super(options) - - this.fftSize = this.blockSize - this.timeCursor = 0 - - this.hannWindow = genHannWindow(this.blockSize) - - // prepare FFT and pre-allocate buffers - this.fft = new FFT(this.fftSize) - this.freqComplexBuffer = this.fft.createComplexArray() - this.freqComplexBufferShifted = this.fft.createComplexArray() - this.timeComplexBuffer = this.fft.createComplexArray() - this.magnitudes = new Float32Array(this.fftSize / 2 + 1) - this.peakIndexes = new Int32Array(this.magnitudes.length) - this.nbPeaks = 0 - } - - processOLA(inputs, outputs, parameters) { - // no automation, take last value - const pitchFactor = parameters.pitchFactor[parameters.pitchFactor.length - 1] - - for (let i = 0; i < this.nbInputs; i++) { - for (let j = 0; j < inputs[i].length; j++) { - // big assumption here: output is symetric to input - let input = inputs[i][j] - let output = outputs[i][j] - - this.applyHannWindow(input) - - this.fft.realTransform(this.freqComplexBuffer, input) - - this.computeMagnitudes() - this.findPeaks() - this.shiftPeaks(pitchFactor) - - this.fft.completeSpectrum(this.freqComplexBufferShifted) - this.fft.inverseTransform(this.timeComplexBuffer, this.freqComplexBufferShifted) - this.fft.fromComplexArray(this.timeComplexBuffer, output) - - this.applyHannWindow(output) - } - } - - this.timeCursor += this.hopSize - } - - /** Apply Hann window in-place */ - applyHannWindow(input) { - for (let i = 0; i < this.blockSize; i++) { - input[i] = input[i] * this.hannWindow[i] - } - } - - /** Compute squared magnitudes for peak finding **/ - computeMagnitudes() { - let i = 0; let j = 0 - while (i < this.magnitudes.length) { - let real = this.freqComplexBuffer[j] - let imag = this.freqComplexBuffer[j + 1] - // no need to sqrt for peak finding - this.magnitudes[i] = real ** 2 + imag ** 2 - i += 1 - j += 2 - } - } - - /** Find peaks in spectrum magnitudes **/ - findPeaks() { - this.nbPeaks = 0 - let i = 2 - let end = this.magnitudes.length - 2 - - while (i < end) { - let mag = this.magnitudes[i] - - if (this.magnitudes[i - 1] >= mag || this.magnitudes[i - 2] >= mag) { - i++ - continue - } - if (this.magnitudes[i + 1] >= mag || this.magnitudes[i + 2] >= mag) { - i++ - continue - } - - this.peakIndexes[this.nbPeaks] = i - this.nbPeaks++ - i += 2 - } - } - - /** Shift peaks and regions of influence by pitchFactor into new specturm */ - shiftPeaks(pitchFactor) { - // zero-fill new spectrum - this.freqComplexBufferShifted.fill(0) - - for (let i = 0; i < this.nbPeaks; i++) { - let peakIndex = this.peakIndexes[i] - let peakIndexShifted = Math.round(peakIndex * pitchFactor) - - if (peakIndexShifted > this.magnitudes.length) { - break - } - - // find region of influence - let startIndex = 0 - let endIndex = this.fftSize - if (i > 0) { - let peakIndexBefore = this.peakIndexes[i - 1] - startIndex = peakIndex - Math.floor((peakIndex - peakIndexBefore) / 2) - } - if (i < this.nbPeaks - 1) { - let peakIndexAfter = this.peakIndexes[i + 1] - endIndex = peakIndex + Math.ceil((peakIndexAfter - peakIndex) / 2) - } - - // shift whole region of influence around peak to shifted peak - let startOffset = startIndex - peakIndex - let endOffset = endIndex - peakIndex - for (let j = startOffset; j < endOffset; j++) { - let binIndex = peakIndex + j - let binIndexShifted = peakIndexShifted + j - - if (binIndexShifted >= this.magnitudes.length) { - break - } - - // apply phase correction - let omegaDelta = 2 * Math.PI * (binIndexShifted - binIndex) / this.fftSize - let phaseShiftReal = Math.cos(omegaDelta * this.timeCursor) - let phaseShiftImag = Math.sin(omegaDelta * this.timeCursor) - - let indexReal = binIndex * 2 - let indexImag = indexReal + 1 - let valueReal = this.freqComplexBuffer[indexReal] - let valueImag = this.freqComplexBuffer[indexImag] - - let valueShiftedReal = valueReal * phaseShiftReal - valueImag * phaseShiftImag - let valueShiftedImag = valueReal * phaseShiftImag + valueImag * phaseShiftReal - - let indexShiftedReal = binIndexShifted * 2 - let indexShiftedImag = indexShiftedReal + 1 - this.freqComplexBufferShifted[indexShiftedReal] += valueShiftedReal - this.freqComplexBufferShifted[indexShiftedImag] += valueShiftedImag - } - } - } -} - -// eslint-disable-next-line no-undef -registerProcessor('phase-vocoder-processor', PhaseVocoderProcessor) - +export default FFT diff --git a/src/renderer/utils/pitch-shifter/ola-processor.js b/src/renderer/plugins/player/pitch-shifter/ola-processor.js similarity index 94% rename from src/renderer/utils/pitch-shifter/ola-processor.js rename to src/renderer/plugins/player/pitch-shifter/ola-processor.js index c712f357..b55dec1c 100644 --- a/src/renderer/utils/pitch-shifter/ola-processor.js +++ b/src/renderer/plugins/player/pitch-shifter/ola-processor.js @@ -1,12 +1,13 @@ const WEBAUDIO_BLOCK_SIZE = 128 /** Overlap-Add Node */ -class OLAProcessor extends window.AudioWorkletProcessor { +class OLAProcessor extends globalThis.AudioWorkletProcessor { constructor(options) { super(options) this.nbInputs = options.numberOfInputs this.nbOutputs = options.numberOfOutputs + this.paused = true this.blockSize = options.processorOptions.blockSize // TODO for now, the only support hop size is the size of a web audio block @@ -88,7 +89,8 @@ class OLAProcessor extends window.AudioWorkletProcessor { /** Read next web audio block to input buffers **/ readInputs(inputs) { // when playback is paused, we may stop receiving new samples - if (inputs[0].length && inputs[0][0].length == 0) { + // if (inputs[0].length && inputs[0][0].length == 0) { + if (!inputs[0].length || !inputs[0][0].length || inputs[0][0][0] == 0) { for (let i = 0; i < this.nbInputs; i++) { for (let j = 0; j < this.inputBuffers[i].length; j++) { this.inputBuffers[i][j].fill(0, this.blockSize) @@ -155,7 +157,8 @@ class OLAProcessor extends window.AudioWorkletProcessor { } process(inputs, outputs, params) { - this.reallocateChannelsIfNeeded(inputs, outputs) + // if (!inputs[0].length || !inputs[0][0].length || inputs[0][0][0] == 0) return true + // this.reallocateChannelsIfNeeded(inputs, outputs) this.readInputs(inputs) this.shiftInputBuffers() diff --git a/src/renderer/plugins/player/pitch-shifter/phase-vocoder.js b/src/renderer/plugins/player/pitch-shifter/phase-vocoder.js new file mode 100644 index 00000000..77b80332 --- /dev/null +++ b/src/renderer/plugins/player/pitch-shifter/phase-vocoder.js @@ -0,0 +1,172 @@ +// https://github.com/olvb/phaze +import FFT from './fft' +import OLAProcessor from './ola-processor' + + +const BUFFERED_BLOCK_SIZE = 4096 + +function genHannWindow(length) { + let win = new Float32Array(length) + for (let i = 0; i < length; i++) { + win[i] = 0.8 * (1 - Math.cos(2 * Math.PI * i / length)) + } + return win +} + +class PhaseVocoderProcessor extends OLAProcessor { + static get parameterDescriptors() { + return [{ + name: 'pitchFactor', + defaultValue: 1.0, + }] + } + + constructor(options) { + options.processorOptions = { + blockSize: BUFFERED_BLOCK_SIZE, + } + super(options) + + this.fftSize = this.blockSize + this.timeCursor = 0 + + this.hannWindow = genHannWindow(this.blockSize) + + // prepare FFT and pre-allocate buffers + this.fft = new FFT(this.fftSize) + this.freqComplexBuffer = this.fft.createComplexArray() + this.freqComplexBufferShifted = this.fft.createComplexArray() + this.timeComplexBuffer = this.fft.createComplexArray() + this.magnitudes = new Float32Array(this.fftSize / 2 + 1) + this.peakIndexes = new Int32Array(this.magnitudes.length) + this.nbPeaks = 0 + } + + processOLA(inputs, outputs, parameters) { + // no automation, take last value + // const pitchFactor = parameters.pitchFactor[parameters.pitchFactor.length - 1] + const pitchFactor = parameters.pitchFactor[0] + + for (let i = 0; i < this.nbInputs; i++) { + for (let j = 0; j < inputs[i].length; j++) { + // big assumption here: output is symetric to input + let input = inputs[i][j] + let output = outputs[i][j] + + this.applyHannWindow(input) + + this.fft.realTransform(this.freqComplexBuffer, input) + + this.computeMagnitudes() + this.findPeaks() + this.shiftPeaks(pitchFactor) + + this.fft.completeSpectrum(this.freqComplexBufferShifted) + this.fft.inverseTransform(this.timeComplexBuffer, this.freqComplexBufferShifted) + this.fft.fromComplexArray(this.timeComplexBuffer, output) + + this.applyHannWindow(output) + } + } + + this.timeCursor += this.hopSize + } + + /** Apply Hann window in-place */ + applyHannWindow(input) { + for (let i = 0; i < this.blockSize; i++) { + input[i] = input[i] * this.hannWindow[i] + } + } + + /** Compute squared magnitudes for peak finding **/ + computeMagnitudes() { + let i = 0; let j = 0 + while (i < this.magnitudes.length) { + let real = this.freqComplexBuffer[j] + let imag = this.freqComplexBuffer[j + 1] + // no need to sqrt for peak finding + this.magnitudes[i] = real ** 2 + imag ** 2 + i += 1 + j += 2 + } + } + + /** Find peaks in spectrum magnitudes **/ + findPeaks() { + this.nbPeaks = 0 + let i = 2 + let end = this.magnitudes.length - 2 + + while (i < end) { + let mag = this.magnitudes[i] + + if (this.magnitudes[i - 1] >= mag || this.magnitudes[i - 2] >= mag) { + i++ + continue + } + if (this.magnitudes[i + 1] >= mag || this.magnitudes[i + 2] >= mag) { + i++ + continue + } + + this.peakIndexes[this.nbPeaks] = i + this.nbPeaks++ + i += 2 + } + } + + /** Shift peaks and regions of influence by pitchFactor into new specturm */ + shiftPeaks(pitchFactor) { + // zero-fill new spectrum + this.freqComplexBufferShifted.fill(0) + + for (let i = 0; i < this.nbPeaks; i++) { + let peakIndex = this.peakIndexes[i] + let peakIndexShifted = Math.round(peakIndex * pitchFactor) + + if (peakIndexShifted > this.magnitudes.length) break + + // find region of influence + let startIndex = 0 + let endIndex = this.fftSize + if (i > 0) { + startIndex = peakIndex - Math.floor((peakIndex - this.peakIndexes[i - 1]) / 2) + } + if (i < this.nbPeaks - 1) { + endIndex = peakIndex + Math.ceil((this.peakIndexes[i + 1] - peakIndex) / 2) + } + + // shift whole region of influence around peak to shifted peak + let startOffset = startIndex - peakIndex + let endOffset = endIndex - peakIndex + for (let j = startOffset; j < endOffset; j++) { + let binIndex = peakIndex + j + let binIndexShifted = peakIndexShifted + j + + if (binIndexShifted >= this.magnitudes.length) break + + // apply phase correction + let omegaDelta = 2 * Math.PI * (binIndexShifted - binIndex) / this.fftSize + let phaseShiftReal = Math.cos(omegaDelta * this.timeCursor) + let phaseShiftImag = Math.sin(omegaDelta * this.timeCursor) + + let indexReal = binIndex * 2 + let indexImag = indexReal + 1 + let valueReal = this.freqComplexBuffer[indexReal] + let valueImag = this.freqComplexBuffer[indexImag] + + let valueShiftedReal = valueReal * phaseShiftReal - valueImag * phaseShiftImag + let valueShiftedImag = valueReal * phaseShiftImag + valueImag * phaseShiftReal + + let indexShiftedReal = binIndexShifted * 2 + let indexShiftedImag = indexShiftedReal + 1 + this.freqComplexBufferShifted[indexShiftedReal] += valueShiftedReal + this.freqComplexBufferShifted[indexShiftedImag] += valueShiftedImag + } + } + } +} + +globalThis.registerProcessor('phase-vocoder-processor', PhaseVocoderProcessor) +