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)
+