create analyzer and analyzer web test
This commit is contained in:
parent
5d672c273e
commit
5dc02bf2e3
22 changed files with 1033 additions and 3 deletions
|
@ -10,10 +10,12 @@ All programs go like this: `npx nx {package script} {package}`
|
||||||
## Test this workspace
|
## Test this workspace
|
||||||
|
|
||||||
Run `npx nx server player-web-test` to see the music player in a minimal demo.
|
Run `npx nx server player-web-test` to see the music player in a minimal demo.
|
||||||
|
Run `npx nx server visualizer-web-test` to see the music player in a minimal demo.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
Run `npx nx build player` to build the player.
|
Run `npx nx build player` to build the player.
|
||||||
|
Run `npx nx build visualizer` to build the player.
|
||||||
|
|
||||||
## Publish
|
## Publish
|
||||||
|
|
||||||
|
|
245
package-lock.json
generated
245
package-lock.json
generated
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "@euterpe/source",
|
"name": "@euterpe.js/source",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@euterpe/source",
|
"name": "@euterpe.js/source",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -19,6 +19,7 @@
|
||||||
"@nx/vite": "^16.2.1",
|
"@nx/vite": "^16.2.1",
|
||||||
"@nx/web": "^16.2.1",
|
"@nx/web": "^16.2.1",
|
||||||
"@nx/workspace": "16.2.1",
|
"@nx/workspace": "16.2.1",
|
||||||
|
"@swc/core": "~1.3.51",
|
||||||
"@types/node": "^20.2.1",
|
"@types/node": "^20.2.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
||||||
"@typescript-eslint/parser": "^5.58.0",
|
"@typescript-eslint/parser": "^5.58.0",
|
||||||
|
@ -31,6 +32,7 @@
|
||||||
"jsdom": "~20.0.3",
|
"jsdom": "~20.0.3",
|
||||||
"nx": "16.2.1",
|
"nx": "16.2.1",
|
||||||
"prettier": "^2.6.2",
|
"prettier": "^2.6.2",
|
||||||
|
"swc-loader": "0.1.15",
|
||||||
"typescript": "~5.0.2",
|
"typescript": "~5.0.2",
|
||||||
"vite": "^4.3.4",
|
"vite": "^4.3.4",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
|
@ -3010,6 +3012,200 @@
|
||||||
"node": ">= 8.0.0"
|
"node": ">= 8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@swc/core": {
|
||||||
|
"version": "1.3.59",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.59.tgz",
|
||||||
|
"integrity": "sha512-ZBw31zd2E5SXiodwGvjQdx5ZC90b2uyX/i2LeMMs8LKfXD86pfOfQac+JVrnyEKDhASXj9icgsF9NXBhaMr3Kw==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/swc"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@swc/core-darwin-arm64": "1.3.59",
|
||||||
|
"@swc/core-darwin-x64": "1.3.59",
|
||||||
|
"@swc/core-linux-arm-gnueabihf": "1.3.59",
|
||||||
|
"@swc/core-linux-arm64-gnu": "1.3.59",
|
||||||
|
"@swc/core-linux-arm64-musl": "1.3.59",
|
||||||
|
"@swc/core-linux-x64-gnu": "1.3.59",
|
||||||
|
"@swc/core-linux-x64-musl": "1.3.59",
|
||||||
|
"@swc/core-win32-arm64-msvc": "1.3.59",
|
||||||
|
"@swc/core-win32-ia32-msvc": "1.3.59",
|
||||||
|
"@swc/core-win32-x64-msvc": "1.3.59"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@swc/helpers": "^0.5.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@swc/helpers": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-darwin-arm64": {
|
||||||
|
"version": "1.3.59",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.59.tgz",
|
||||||
|
"integrity": "sha512-AnqWFBgEKHP0jb4iZqx7eVQT9/rX45+DE4Ox7GpwCahUKxxrsDLyXzKhwLwQuAjUvtu5JcSB77szKpPGDM49fQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-darwin-x64": {
|
||||||
|
"version": "1.3.59",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.59.tgz",
|
||||||
|
"integrity": "sha512-iqDs+yii9mOsmpJez82SEi4d4prWDRlapHxKnDVJ0x1AqRo41vIq8t3fujrvCHYU5VQgOYGh4ooXQpaP2H3B2A==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-linux-arm-gnueabihf": {
|
||||||
|
"version": "1.3.59",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.59.tgz",
|
||||||
|
"integrity": "sha512-PB0PP+SgkCSd/kYmltnPiGv42cOSaih1OjXCEjxvNwUFEmWqluW6uGdWaNiR1LoYMxhcHZTc336jL2+O3l6p0Q==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-linux-arm64-gnu": {
|
||||||
|
"version": "1.3.59",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.59.tgz",
|
||||||
|
"integrity": "sha512-Ol/JPszWZ+OZ44FOdJe35TfJ1ckG4pYaisZJ4E7PzfwfVe2ygX85C5WWR4e5L0Y1zFvzpcI7gdyC2wzcXk4Cig==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-linux-arm64-musl": {
|
||||||
|
"version": "1.3.59",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.59.tgz",
|
||||||
|
"integrity": "sha512-PtTTtGbj9GiY5gJdoSFL2A0vL6BRaS1haAhp6g3hZvLDkTTg+rJURmzwBMMjaQlnGC62x/lLf6MoszHG/05//Q==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-linux-x64-gnu": {
|
||||||
|
"version": "1.3.59",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.59.tgz",
|
||||||
|
"integrity": "sha512-XBW9AGi0YsIN76IfesnDSBn/5sjR69J75KUNte8sH6seYlHJ0/kblqUMbUcfr0CiGoJadbzAZeKZZmfN7EsHpg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-linux-x64-musl": {
|
||||||
|
"version": "1.3.59",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.59.tgz",
|
||||||
|
"integrity": "sha512-Cy5E939SdWPQ34cg6UABNO0RyEe0FuWqzZ/GLKtK11Ir4fjttVlucZiY59uQNyUVUc8T2qE0VBFCyD/zYGuHtg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-win32-arm64-msvc": {
|
||||||
|
"version": "1.3.59",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.59.tgz",
|
||||||
|
"integrity": "sha512-z5ZJxizRvRoSAaevRIi3YjQh74OFWEIhonSDWNdqDL7RbjEivcatYcG7OikH6s+rtPhOcwNm3PbGV2Prcgh/gg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-win32-ia32-msvc": {
|
||||||
|
"version": "1.3.59",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.59.tgz",
|
||||||
|
"integrity": "sha512-vxpsn+hrKAhi5YusQfB/JXUJJVX40rIRE/L49ilBEqdbH8Khkoego6AD+2vWqTdJcUHo1WiAIAEZ0rTsjyorLQ==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-win32-x64-msvc": {
|
||||||
|
"version": "1.3.59",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.59.tgz",
|
||||||
|
"integrity": "sha512-Ris/cJbURylcLwqz4RZUUBCEGsuaIHOJsvf69W5pGKHKBryVoOTNhBKpo3Km2hoAi5qFQ/ou0trAT4hBsVPZvQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@swc/helpers": {
|
"node_modules/@swc/helpers": {
|
||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz",
|
||||||
|
@ -3989,6 +4185,15 @@
|
||||||
"tweetnacl": "^0.14.3"
|
"tweetnacl": "^0.14.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/big.js": {
|
||||||
|
"version": "5.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
||||||
|
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/binary-extensions": {
|
"node_modules/binary-extensions": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||||
|
@ -4947,6 +5152,15 @@
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/emojis-list": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/end-of-stream": {
|
"node_modules/end-of-stream": {
|
||||||
"version": "1.4.4",
|
"version": "1.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||||
|
@ -6705,6 +6919,20 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/loader-utils": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"big.js": "^5.2.2",
|
||||||
|
"emojis-list": "^3.0.0",
|
||||||
|
"json5": "^2.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/local-pkg": {
|
"node_modules/local-pkg": {
|
||||||
"version": "0.4.3",
|
"version": "0.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz",
|
||||||
|
@ -8276,6 +8504,19 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/swc-loader": {
|
||||||
|
"version": "0.1.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/swc-loader/-/swc-loader-0.1.15.tgz",
|
||||||
|
"integrity": "sha512-cn1WPIeQJvXM4bbo3OwdEIapsQ4uUGOfyFj0h2+2+brT0k76DCGnZXDE2KmcqTd2JSQ+b61z2NPMib7eEwMYYw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"loader-utils": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@swc/core": "^1.2.52",
|
||||||
|
"webpack": ">=2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/symbol-tree": {
|
"node_modules/symbol-tree": {
|
||||||
"version": "3.2.4",
|
"version": "3.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"publish-player": "cd dist/packages/player && npm publish --access=public"
|
"publish-player": "cd dist/packages/player && npm publish --access=public",
|
||||||
|
"publish-visualizer": "cd dist/packages/visualizer && npm publish --access=public"
|
||||||
},
|
},
|
||||||
"private": false,
|
"private": false,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -14,6 +15,7 @@
|
||||||
"@nx/vite": "^16.2.1",
|
"@nx/vite": "^16.2.1",
|
||||||
"@nx/web": "^16.2.1",
|
"@nx/web": "^16.2.1",
|
||||||
"@nx/workspace": "16.2.1",
|
"@nx/workspace": "16.2.1",
|
||||||
|
"@swc/core": "~1.3.51",
|
||||||
"@types/node": "^20.2.1",
|
"@types/node": "^20.2.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
||||||
"@typescript-eslint/parser": "^5.58.0",
|
"@typescript-eslint/parser": "^5.58.0",
|
||||||
|
@ -26,6 +28,7 @@
|
||||||
"jsdom": "~20.0.3",
|
"jsdom": "~20.0.3",
|
||||||
"nx": "16.2.1",
|
"nx": "16.2.1",
|
||||||
"prettier": "^2.6.2",
|
"prettier": "^2.6.2",
|
||||||
|
"swc-loader": "0.1.15",
|
||||||
"typescript": "~5.0.2",
|
"typescript": "~5.0.2",
|
||||||
"vite": "^4.3.4",
|
"vite": "^4.3.4",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
|
|
33
packages/visualizer-web-test/.eslintrc.json
Normal file
33
packages/visualizer-web-test/.eslintrc.json
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"../../.eslintrc.json"
|
||||||
|
],
|
||||||
|
"ignorePatterns": [
|
||||||
|
"!**/*"
|
||||||
|
],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"*.ts",
|
||||||
|
"*.tsx",
|
||||||
|
"*.js",
|
||||||
|
"*.jsx"
|
||||||
|
],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"*.ts",
|
||||||
|
"*.tsx"
|
||||||
|
],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"*.js",
|
||||||
|
"*.jsx"
|
||||||
|
],
|
||||||
|
"rules": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
38
packages/visualizer-web-test/index.html
Normal file
38
packages/visualizer-web-test/index.html
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>VisualizerWebTest</title>
|
||||||
|
<base href="/" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||||
|
<link rel="stylesheet" href="/src/styles.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="viz-wrapper">
|
||||||
|
<svg id="trapnation-canvas" viewBox="0 0 500 500" preserveAspectRatio="none"></svg>
|
||||||
|
<svg id="bar-canvas" viewBox="0 0 500 500" preserveAspectRatio="none"></svg>
|
||||||
|
</div>
|
||||||
|
<div class="player-wrapper">
|
||||||
|
<audio src="" id="audio"></audio>
|
||||||
|
<button id="play">Play</button>
|
||||||
|
<button id="pause">Pause</button>
|
||||||
|
<button id="toggle-play">Toggle Pause/Play</button>
|
||||||
|
<p id="current">-:--</p>
|
||||||
|
<input type="range" min="0" max="10" value="0" id="seek" step="0.01">
|
||||||
|
<p id="duration">-:--</p>
|
||||||
|
<span>
|
||||||
|
<input type="range" min="0" max="1" value="1" id="volume" step="0.01">
|
||||||
|
</span>
|
||||||
|
<button id="mute">Mute</button>
|
||||||
|
<button id="unmute">Unmute</button>
|
||||||
|
<button id="toggle-mute">Toggle Mute</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
70
packages/visualizer-web-test/project.json
Normal file
70
packages/visualizer-web-test/project.json
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
{
|
||||||
|
"name": "visualizer-web-test",
|
||||||
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
|
"projectType": "application",
|
||||||
|
"sourceRoot": "packages/visualizer-web-test/src",
|
||||||
|
"tags": [],
|
||||||
|
"targets": {
|
||||||
|
"build": {
|
||||||
|
"executor": "@nx/vite:build",
|
||||||
|
"outputs": [
|
||||||
|
"{options.outputPath}"
|
||||||
|
],
|
||||||
|
"defaultConfiguration": "production",
|
||||||
|
"options": {
|
||||||
|
"outputPath": "dist/packages/visualizer-web-test"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"development": {
|
||||||
|
"mode": "development"
|
||||||
|
},
|
||||||
|
"production": {
|
||||||
|
"mode": "production"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"executor": "@nx/vite:dev-server",
|
||||||
|
"defaultConfiguration": "development",
|
||||||
|
"options": {
|
||||||
|
"buildTarget": "visualizer-web-test:build"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"development": {
|
||||||
|
"buildTarget": "visualizer-web-test:build:development",
|
||||||
|
"hmr": true
|
||||||
|
},
|
||||||
|
"production": {
|
||||||
|
"buildTarget": "visualizer-web-test:build:production",
|
||||||
|
"hmr": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"preview": {
|
||||||
|
"executor": "@nx/vite:preview-server",
|
||||||
|
"defaultConfiguration": "development",
|
||||||
|
"options": {
|
||||||
|
"buildTarget": "visualizer-web-test:build"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"development": {
|
||||||
|
"buildTarget": "visualizer-web-test:build:development"
|
||||||
|
},
|
||||||
|
"production": {
|
||||||
|
"buildTarget": "visualizer-web-test:build:production"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"executor": "@nx/linter:eslint",
|
||||||
|
"outputs": [
|
||||||
|
"{options.outputFile}"
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"lintFilePatterns": [
|
||||||
|
"packages/visualizer-web-test/**/*.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
packages/visualizer-web-test/public/favicon.ico
Normal file
BIN
packages/visualizer-web-test/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
0
packages/visualizer-web-test/src/assets/.gitkeep
Normal file
0
packages/visualizer-web-test/src/assets/.gitkeep
Normal file
82
packages/visualizer-web-test/src/main.ts
Normal file
82
packages/visualizer-web-test/src/main.ts
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
import { MusicPlayerBuilder } from "@euterpe/player";
|
||||||
|
import { AudioVisualBuilder, SmoothingAlgorythm, ShapeType } from "@euterpe/visualizer";
|
||||||
|
const audio_el = document.querySelector("#audio") as HTMLAudioElement
|
||||||
|
const music_player_builder = MusicPlayerBuilder(audio_el)
|
||||||
|
music_player_builder.start()
|
||||||
|
const analyser_node = music_player_builder.add_analyser()
|
||||||
|
const music_player = music_player_builder.build()
|
||||||
|
music_player.change_volume(1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const trapnation_visual_builder = AudioVisualBuilder(analyser_node, document.querySelector("#trapnation-canvas") as SVGSVGElement)
|
||||||
|
trapnation_visual_builder.start()
|
||||||
|
trapnation_visual_builder.set_fft_size(8192)
|
||||||
|
trapnation_visual_builder.set_fft_data_tresholds({ to_fft_range_i: 3, point_count_i: 50, fft_multiplier_i: 1, fft_offset_i: -80 })
|
||||||
|
trapnation_visual_builder.set_fft_time_smoothing(0.6)
|
||||||
|
//If not using typescript enums, CatmullRom = number 2
|
||||||
|
trapnation_visual_builder.set_smoothing_algorythm(SmoothingAlgorythm.CatmullRom)
|
||||||
|
const trapnation_visual = trapnation_visual_builder.build(ShapeType.Circle)
|
||||||
|
|
||||||
|
const bar_visual_builder = AudioVisualBuilder(analyser_node, document.querySelector("#bar-canvas") as SVGSVGElement)
|
||||||
|
bar_visual_builder.start()
|
||||||
|
bar_visual_builder.set_fft_data_tresholds({ to_fft_range_i: 10, point_count_i: 10, fft_multiplier_i: 1.5, fft_offset_i: 50 })
|
||||||
|
bar_visual_builder.set_fft_time_smoothing(0.8)
|
||||||
|
//If not using typescript enums, CatmullRom = number 2
|
||||||
|
bar_visual_builder.set_smoothing_algorythm(SmoothingAlgorythm.Linear)
|
||||||
|
const bar_visual = trapnation_visual_builder.build(ShapeType.Line)
|
||||||
|
|
||||||
|
|
||||||
|
trapnation_visual.draw()
|
||||||
|
bar_visual.draw()
|
||||||
|
/*
|
||||||
|
* The player part
|
||||||
|
*/
|
||||||
|
music_player.try_new_song_async(encodeURI("http://127.0.0.1:4200/nuphory - NVISION (EXTENDED MIX).ogg"))
|
||||||
|
.then(() => {
|
||||||
|
let is_seeking = false
|
||||||
|
document.querySelector("#play")?.addEventListener("click", () => {
|
||||||
|
music_player.play_async()
|
||||||
|
.then(() => { console.log("Playing!") }, (e) => alert("Failed to play, " + e))
|
||||||
|
})
|
||||||
|
document.querySelector("#pause")?.addEventListener("click", () => {
|
||||||
|
music_player.pause()
|
||||||
|
})
|
||||||
|
document.querySelector("#mute")?.addEventListener("click", () => {
|
||||||
|
music_player.mute()
|
||||||
|
})
|
||||||
|
document.querySelector("#unmute")?.addEventListener("click", () => {
|
||||||
|
music_player.unmute()
|
||||||
|
})
|
||||||
|
document.querySelector("#toggle-mute")?.addEventListener("click", () => {
|
||||||
|
music_player.mute_toggle()
|
||||||
|
})
|
||||||
|
document.querySelector("#toggle-play")?.addEventListener("click", () => {
|
||||||
|
music_player.play_toggle_async().then((s) => console.log("toggled play/pause"), (e) => alert("failed to toggle pause/play!" + e))
|
||||||
|
})
|
||||||
|
document.querySelector("#volume")?.addEventListener("input", (e) => {
|
||||||
|
music_player.change_volume(e.target?.valueAsNumber)
|
||||||
|
})
|
||||||
|
document.querySelector("#seek")?.addEventListener("mousedown", (e) => {
|
||||||
|
is_seeking = true;
|
||||||
|
})
|
||||||
|
document.querySelector("#seek")?.addEventListener("mouseup", (e) => {
|
||||||
|
music_player.try_seek_async(e.target?.valueAsNumber).then(() => { console.log("seeked to " + e.target?.valueAsNumber) }, () => {
|
||||||
|
alert("Failed seeking! " + e)
|
||||||
|
})
|
||||||
|
is_seeking = false
|
||||||
|
})
|
||||||
|
// Subscriptions to AudioContext changes, eg. time..
|
||||||
|
music_player.subscribe_to_formatted_duration_time((time) => {
|
||||||
|
document.querySelector("#duration").innerHTML = time
|
||||||
|
document.querySelector("#seek").max = "" + music_player.get_current_duration()
|
||||||
|
})
|
||||||
|
music_player.subscribe_to_formatted_current_time_tick((time) => {
|
||||||
|
document.querySelector("#current").innerHTML = time
|
||||||
|
})
|
||||||
|
music_player.subscribe_to_time_tick((time) => {
|
||||||
|
if (is_seeking) return
|
||||||
|
document.querySelector("#seek").value = "" + time
|
||||||
|
})
|
||||||
|
|
||||||
|
}, (e) => console.log(e))
|
30
packages/visualizer-web-test/src/styles.css
Normal file
30
packages/visualizer-web-test/src/styles.css
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#volume{
|
||||||
|
transform: rotate(270deg);
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
width: 100vw;
|
||||||
|
height:100vh;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.player-wrapper{
|
||||||
|
width:50vw;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.viz-wrapper{
|
||||||
|
width:50vw;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
#trapnation-canvas{
|
||||||
|
width:100%;
|
||||||
|
fill: grey;
|
||||||
|
stroke: black;
|
||||||
|
}
|
||||||
|
.wrapper div{
|
||||||
|
width:100%;
|
||||||
|
}
|
29
packages/visualizer-web-test/tsconfig.json
Normal file
29
packages/visualizer-web-test/tsconfig.json
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"files": [],
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": [
|
||||||
|
"ESNext",
|
||||||
|
"DOM"
|
||||||
|
],
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"strict": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"types": [
|
||||||
|
"vite/client"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src"
|
||||||
|
]
|
||||||
|
}
|
33
packages/visualizer-web-test/vite.config.ts
Normal file
33
packages/visualizer-web-test/vite.config.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/// <reference types="vitest" />
|
||||||
|
import { defineConfig } from "vite"
|
||||||
|
|
||||||
|
import viteTsConfigPaths from "vite-tsconfig-paths"
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
cacheDir: "../../node_modules/.vite/visualizer-web-test",
|
||||||
|
|
||||||
|
server: {
|
||||||
|
port: 4200,
|
||||||
|
host: "localhost"
|
||||||
|
},
|
||||||
|
|
||||||
|
preview: {
|
||||||
|
port: 4300,
|
||||||
|
host: "localhost"
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
viteTsConfigPaths({
|
||||||
|
root: "../../"
|
||||||
|
})
|
||||||
|
]
|
||||||
|
|
||||||
|
// Uncomment this if you are using workers.
|
||||||
|
// worker: {
|
||||||
|
// plugins: [
|
||||||
|
// viteTsConfigPaths({
|
||||||
|
// root: '../../',
|
||||||
|
// }),
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
})
|
33
packages/visualizer/.eslintrc.json
Normal file
33
packages/visualizer/.eslintrc.json
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"../../.eslintrc.json"
|
||||||
|
],
|
||||||
|
"ignorePatterns": [
|
||||||
|
"!**/*"
|
||||||
|
],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"*.ts",
|
||||||
|
"*.tsx",
|
||||||
|
"*.js",
|
||||||
|
"*.jsx"
|
||||||
|
],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"*.ts",
|
||||||
|
"*.tsx"
|
||||||
|
],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"*.js",
|
||||||
|
"*.jsx"
|
||||||
|
],
|
||||||
|
"rules": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
7
packages/visualizer/README.md
Normal file
7
packages/visualizer/README.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# Visualizer
|
||||||
|
|
||||||
|
Euterpe Visualizer is an extensive Audio visual library for the web. Using SVG elements instead of canvas, it allows to leverage the power of GPU to do the actual drawing, whilst allowing and ease of use as opposed to the canvas counter part.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Run `nx build visualizer` to build the library.
|
5
packages/visualizer/package.json
Normal file
5
packages/visualizer/package.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"name": "@euterpe/visualizer",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"type": "commonjs"
|
||||||
|
}
|
40
packages/visualizer/project.json
Normal file
40
packages/visualizer/project.json
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"name": "visualizer",
|
||||||
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
|
"sourceRoot": "packages/visualizer/src",
|
||||||
|
"projectType": "library",
|
||||||
|
"targets": {
|
||||||
|
"build": {
|
||||||
|
"executor": "@nx/js:tsc",
|
||||||
|
"outputs": [
|
||||||
|
"{options.outputPath}"
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"outputPath": "dist/packages/visualizer",
|
||||||
|
"main": "packages/visualizer/src/index.ts",
|
||||||
|
"tsConfig": "packages/visualizer/tsconfig.lib.json",
|
||||||
|
"assets": [
|
||||||
|
"packages/visualizer/*.md"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"publish": {
|
||||||
|
"command": "node tools/scripts/publish.mjs visualizer {args.ver} {args.tag}",
|
||||||
|
"dependsOn": [
|
||||||
|
"build"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"executor": "@nx/linter:eslint",
|
||||||
|
"outputs": [
|
||||||
|
"{options.outputFile}"
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"lintFilePatterns": [
|
||||||
|
"packages/visualizer/**/*.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": []
|
||||||
|
}
|
348
packages/visualizer/src/index.ts
Normal file
348
packages/visualizer/src/index.ts
Normal file
|
@ -0,0 +1,348 @@
|
||||||
|
export enum SmoothingAlgorythm {
|
||||||
|
Linear,
|
||||||
|
BezierPerpendicular,
|
||||||
|
CatmullRom,
|
||||||
|
BezierWeighted,
|
||||||
|
}
|
||||||
|
export enum ShapeType {
|
||||||
|
Circle,
|
||||||
|
Line,
|
||||||
|
/*To be Implmeneted
|
||||||
|
Waveform,
|
||||||
|
FullSongWaveForm
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
type Point = {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
}
|
||||||
|
type Shape = {
|
||||||
|
shape_type: ShapeType,
|
||||||
|
//Algo-rythm, because this is about music. Get it? xd
|
||||||
|
smoothing_algorythm: SmoothingAlgorythm
|
||||||
|
points: Point[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AudioVisual(
|
||||||
|
analyzer_node: AnalyserNode,
|
||||||
|
svg_injecting_element: SVGSVGElement,
|
||||||
|
shape: Shape,
|
||||||
|
buffer_length: number,
|
||||||
|
fft_multiplier: number,
|
||||||
|
fft_offset: number,
|
||||||
|
from_fft_range: number,
|
||||||
|
to_fft_range: number,
|
||||||
|
point_count: number,
|
||||||
|
canvas_height: number,
|
||||||
|
canvas_width: number,
|
||||||
|
) {
|
||||||
|
let fft_data = new Float32Array(buffer_length)
|
||||||
|
|
||||||
|
function get_cured_frequency_data() {
|
||||||
|
fft_data = new Float32Array(buffer_length)
|
||||||
|
analyzer_node.getFloatFrequencyData(fft_data)
|
||||||
|
const from = Math.round((point_count / 100) * from_fft_range)
|
||||||
|
const to = Math.round(buffer_length - (buffer_length / 100) * to_fft_range)
|
||||||
|
const squeeze_factor = Math.round((buffer_length - to) / point_count)
|
||||||
|
|
||||||
|
const return_array = new Array(point_count)
|
||||||
|
for (let i = 0; i < point_count + 1; i++) {
|
||||||
|
return_array[i] = fft_data[from + i * squeeze_factor]
|
||||||
|
}
|
||||||
|
return return_array
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalise_perpendicular_anchors(x: number, y: number) {
|
||||||
|
const magnitude = Math.sqrt(x * x + y * y)
|
||||||
|
return [x / magnitude, y / magnitude]
|
||||||
|
}
|
||||||
|
|
||||||
|
function create_perpendicular_anchors(arr: { x: number, y: number }[]) {
|
||||||
|
const anchors = []
|
||||||
|
switch (shape.shape_type) {
|
||||||
|
case ShapeType.Circle: {
|
||||||
|
const pointDistance = 7
|
||||||
|
for (let curPoint = 0; curPoint < arr.length; curPoint++) {
|
||||||
|
const [dx, dy] = normalise_perpendicular_anchors(arr[curPoint].x, arr[curPoint].y)
|
||||||
|
const perpendicular = [-dy, dx]
|
||||||
|
anchors.push({
|
||||||
|
leftAnchor: {
|
||||||
|
x: arr[curPoint].x + pointDistance * perpendicular[0],
|
||||||
|
y: arr[curPoint].y + pointDistance * perpendicular[1],
|
||||||
|
},
|
||||||
|
rightAnchor: {
|
||||||
|
x: arr[curPoint].x - pointDistance * perpendicular[0],
|
||||||
|
y: arr[curPoint].y - pointDistance * perpendicular[1],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case ShapeType.Line: {
|
||||||
|
const pointDistance = canvas_width / arr.length
|
||||||
|
for (let curPoint = 0; curPoint < arr.length; curPoint++) {
|
||||||
|
anchors.push({
|
||||||
|
leftAnchor: {
|
||||||
|
x: pointDistance * curPoint - pointDistance / 3,
|
||||||
|
y: arr[curPoint].y,
|
||||||
|
},
|
||||||
|
rightAnchor: {
|
||||||
|
x: pointDistance * curPoint + pointDistance / 3,
|
||||||
|
y: arr[curPoint].y,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return anchors
|
||||||
|
}
|
||||||
|
|
||||||
|
function catmull_rom_smooth(arr: { x: number, y: number }[], k: number) {
|
||||||
|
if (k == null) k = 1
|
||||||
|
const last = arr.length - 2
|
||||||
|
|
||||||
|
let path = "M" + [arr[0].x, arr[0].y]
|
||||||
|
|
||||||
|
for (let i = 0; i < arr.length - 1; i++) {
|
||||||
|
|
||||||
|
const x0 = i ? arr[i - 1].x : arr[0].x
|
||||||
|
const y0 = i ? arr[i - 1].y : arr[0].y
|
||||||
|
|
||||||
|
const x1 = arr[i].x
|
||||||
|
const y1 = arr[i].y
|
||||||
|
|
||||||
|
const x2 = arr[i + 1].x
|
||||||
|
const y2 = arr[i + 1].y
|
||||||
|
|
||||||
|
let subx = y2
|
||||||
|
let suby = y2
|
||||||
|
//Makes the last line before Z a bit less jarring
|
||||||
|
if (shape.shape_type == ShapeType.Circle) {
|
||||||
|
subx = arr[0].x
|
||||||
|
suby = arr[0].y
|
||||||
|
}
|
||||||
|
const x3 = i !== last ? arr[i + 2].x : subx
|
||||||
|
const y3 = i !== last ? arr[i + 2].y : suby
|
||||||
|
|
||||||
|
const cp1x = x1 + (x2 - x0) / 6 * k
|
||||||
|
const cp1y = y1 + (y2 - y0) / 6 * k
|
||||||
|
|
||||||
|
const cp2x = x2 - (x3 - x1) / 6 * k
|
||||||
|
const cp2y = y2 - (y3 - y1) / 6 * k
|
||||||
|
|
||||||
|
path += "C" + [cp1x, cp1y, cp2x, cp2y, x2, y2]
|
||||||
|
}
|
||||||
|
path += " Z"
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
function mutate_points() {
|
||||||
|
const mutated_points = []
|
||||||
|
const frequency_data = get_cured_frequency_data()
|
||||||
|
const out_range = [0, canvas_height]
|
||||||
|
const in_range = [-165, -30]
|
||||||
|
switch (shape.shape_type) {
|
||||||
|
case ShapeType.Line: {
|
||||||
|
for (let i = 0; i < frequency_data.length - 1; i++) {
|
||||||
|
mutated_points.push({
|
||||||
|
x: shape.points[i].x /** ((Math.max(FFTDataArray[i] + 100)) * 4)*/,
|
||||||
|
y: shape.points[i].y - convert_range(frequency_data[i] * fft_multiplier + fft_offset, in_range, out_range),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case ShapeType.Circle: {
|
||||||
|
for (let i = 0; i < frequency_data.length - 1; i++) {
|
||||||
|
const new_i = i > (frequency_data.length - 1) / 2 ? frequency_data.length - 1 - i : i
|
||||||
|
mutated_points.push({
|
||||||
|
x: shape.points[i].x * Math.max((frequency_data[new_i] * fft_multiplier + fft_offset) / 50, 1) + canvas_width / 2,
|
||||||
|
y: shape.points[i].y * Math.max((frequency_data[new_i] * fft_multiplier + fft_offset) / 50, 1) + canvas_height / 2,
|
||||||
|
})
|
||||||
|
/* TODO: IMPLEMENT SCALING TO BEAT
|
||||||
|
this.injectingHTMLElement.parentElement.style.transform = `scale(${(100 + Math.max((frequencyData[2] * 2 + 130) / 5, 1)) / 100})`
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mutated_points
|
||||||
|
}
|
||||||
|
|
||||||
|
function convert_range(value: number, r1: number[], r2: number[]) {
|
||||||
|
if (!isFinite(value)) return 0
|
||||||
|
return ((value - r1[0]) * (r2[1] - r2[0])) / (r1[1] - r1[0]) + r2[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
function create_svg_element() {
|
||||||
|
let path
|
||||||
|
const arr = mutate_points()
|
||||||
|
switch (shape.shape_type) {
|
||||||
|
case ShapeType.Line: {
|
||||||
|
path = `M ${0} ${canvas_height} `
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case ShapeType.Circle: {
|
||||||
|
path = `M ${arr[0].x} ${arr[0].y} `
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (shape.smoothing_algorythm) {
|
||||||
|
case SmoothingAlgorythm.Linear: {
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
path += `L ${arr[i].x},${arr[i].y} `
|
||||||
|
}
|
||||||
|
if (shape.shape_type == ShapeType.Line) {
|
||||||
|
path += `L ${this.canvasWidth} ${this.canvasHeight / 2} `
|
||||||
|
//path += `L ${canvas_width} ${canvas_height} `
|
||||||
|
}
|
||||||
|
path += `Z `
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case SmoothingAlgorythm.BezierPerpendicular: {
|
||||||
|
const anchors = create_perpendicular_anchors(arr)
|
||||||
|
|
||||||
|
for (let i = 1; i < arr.length; i++) {
|
||||||
|
path += `C ${anchors[i - 1].rightAnchor.x} ${anchors[i - 1].rightAnchor.y} ${anchors[i].leftAnchor.x} ${anchors[i].leftAnchor.y} ${arr[i].x} ${arr[i].y} `
|
||||||
|
}
|
||||||
|
if (shape.shape_type == ShapeType.Line) {
|
||||||
|
//path += `L ${this.canvasWidth} ${this.canvasHeight / 2} `
|
||||||
|
path += `L ${canvas_width} ${canvas_height} `
|
||||||
|
}
|
||||||
|
path += `Z `
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case SmoothingAlgorythm.BezierWeighted: {
|
||||||
|
/*THIS IS VERY MUCH BROKEN ATM :(
|
||||||
|
for (let i = 2; i < arr.length; i++) {
|
||||||
|
const end = [arr.x[i], arr.y[i]] // the current point is the end of this segment of the curve
|
||||||
|
path += `C ${startControl[0]} ${startControl[1]} ${endControl[0]} ${endControl[1]} ${end[0]} ${end[1]}`
|
||||||
|
}*/
|
||||||
|
console.error("BezierWeighted not implemented yet...")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case SmoothingAlgorythm.CatmullRom: {
|
||||||
|
path = catmull_rom_smooth(arr, 1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return `<path width="100%" height="100%" d="${path}"/>`
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
analyzer_node.getFloatFrequencyData(fft_data)
|
||||||
|
svg_injecting_element.innerHTML = create_svg_element()
|
||||||
|
requestAnimationFrame(draw.bind(AudioVisual))
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
draw,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function AudioVisualBuilder(analyzer_node: AnalyserNode, svg_injecting_element: SVGSVGElement) {
|
||||||
|
let canvas_height: number
|
||||||
|
let canvas_width: number
|
||||||
|
let buffer_length = analyzer_node.frequencyBinCount
|
||||||
|
let smoothing_algorythm = SmoothingAlgorythm.Linear
|
||||||
|
let fft_time_smoothing = 0.1
|
||||||
|
let fft_size = 4096
|
||||||
|
let fft_multipier = 1.5
|
||||||
|
let fft_offset = -50
|
||||||
|
let from_fft_range = 0
|
||||||
|
let to_fft_range = 100
|
||||||
|
let point_count: number
|
||||||
|
let scale_to_beat = false
|
||||||
|
let shape: Shape
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
canvas_width = svg_injecting_element.viewBox.baseVal.width // viewbox does exist on svg element, ignore error...
|
||||||
|
canvas_height = svg_injecting_element.viewBox.baseVal.height
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_fft_time_smoothing(fft_time_smoothing_i: number) {
|
||||||
|
analyzer_node.smoothingTimeConstant = fft_time_smoothing = fft_time_smoothing_i
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
function set_fft_size(fft_size_i: number) {
|
||||||
|
if (!(fft_size && !(fft_size_i & (fft_size_i - 1)))) throw Error("fft_size not power of two")
|
||||||
|
analyzer_node.fftSize = fft_size = fft_size_i
|
||||||
|
buffer_length = analyzer_node.frequencyBinCount
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
function set_fft_data_tresholds({ from_fft_range_i = 0, to_fft_range_i = 100, point_count_i = Math.round((buffer_length / 100) * (from_fft_range_i - to_fft_range_i)), fft_multiplier_i = 2, fft_offset_i = -50 }) {
|
||||||
|
from_fft_range = from_fft_range_i
|
||||||
|
to_fft_range = to_fft_range_i
|
||||||
|
point_count = point_count_i
|
||||||
|
fft_multipier = fft_multiplier_i
|
||||||
|
fft_offset = fft_offset_i
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
function set_smoothing_algorythm(algorythm: SmoothingAlgorythm) {
|
||||||
|
smoothing_algorythm = algorythm
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
function enable_scaling_to_beat(enable = false) {
|
||||||
|
scale_to_beat = enable
|
||||||
|
}
|
||||||
|
function create_shape(shape_type: ShapeType): Shape {
|
||||||
|
const point_amount = get_cured_frequency_data(analyzer_node, buffer_length, from_fft_range, to_fft_range, point_count).length
|
||||||
|
let new_shape: Shape
|
||||||
|
switch (shape_type) {
|
||||||
|
case ShapeType.Line: {
|
||||||
|
const points = []
|
||||||
|
for (let i = 0; i < point_amount; i++) {
|
||||||
|
points.push({
|
||||||
|
x: (canvas_width / point_amount) * i,
|
||||||
|
y: canvas_height / 2 - (0 / point_amount) * i,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
new_shape = { shape_type, points, smoothing_algorythm }
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case ShapeType.Circle: {
|
||||||
|
const points = []
|
||||||
|
const radius = canvas_height > canvas_width ? canvas_height / 5 : canvas_width / 5
|
||||||
|
for (let i = 0; i < point_amount; i++) {
|
||||||
|
points.push({
|
||||||
|
x: Math.cos(((2 * Math.PI) / point_amount) * i - Math.PI / 2) * radius,
|
||||||
|
y: Math.sin(((2 * Math.PI) / point_amount) * i - Math.PI / 2) * radius,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
new_shape = { shape_type, points, smoothing_algorythm }
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shape = new_shape
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
function get_cured_frequency_data(analyzer_node: AnalyserNode, buffer_length: number, from_range: number, to_range: number, point_count: number) {
|
||||||
|
const fft_data_array = new Float32Array(buffer_length)
|
||||||
|
analyzer_node.getFloatFrequencyData(fft_data_array)
|
||||||
|
const from = Math.round((point_count / 100) * from_range)
|
||||||
|
const to = Math.round(buffer_length - (buffer_length / 100) * to_range)
|
||||||
|
const squeezeFactor = Math.round((buffer_length - to) / point_count)
|
||||||
|
|
||||||
|
const return_array = new Array(point_count)
|
||||||
|
for (let i = 0; i < point_count; i++) {
|
||||||
|
return_array[i] = fft_data_array[from + i * squeezeFactor]
|
||||||
|
}
|
||||||
|
return return_array
|
||||||
|
}
|
||||||
|
function build(shape_type: ShapeType) {
|
||||||
|
create_shape(shape_type)
|
||||||
|
return AudioVisual(analyzer_node, svg_injecting_element, shape, buffer_length, fft_multipier, fft_offset, from_fft_range, to_fft_range, point_count, canvas_height, canvas_width)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
start,
|
||||||
|
set_fft_size,
|
||||||
|
enable_scaling_to_beat,
|
||||||
|
set_fft_time_smoothing,
|
||||||
|
set_fft_data_tresholds,
|
||||||
|
set_smoothing_algorythm,
|
||||||
|
build
|
||||||
|
}
|
||||||
|
}
|
3
packages/visualizer/src/lib/visualizer.ts
Normal file
3
packages/visualizer/src/lib/visualizer.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export function visualizer(): string {
|
||||||
|
return "visualizer"
|
||||||
|
}
|
20
packages/visualizer/tsconfig.json
Normal file
20
packages/visualizer/tsconfig.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noImplicitThis": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"files": [],
|
||||||
|
"include": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.lib.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
10
packages/visualizer/tsconfig.lib.json
Normal file
10
packages/visualizer/tsconfig.lib.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"declaration": true,
|
||||||
|
"types": ["node"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts"],
|
||||||
|
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
|
||||||
|
}
|
|
@ -20,6 +20,9 @@
|
||||||
"paths": {
|
"paths": {
|
||||||
"@euterpe/player": [
|
"@euterpe/player": [
|
||||||
"packages/player/src/index.ts"
|
"packages/player/src/index.ts"
|
||||||
|
],
|
||||||
|
"@euterpe/visualizer": [
|
||||||
|
"packages/visualizer/src/index.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue