mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-14 06:43:47 -04:00
Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 684216bf8c | |||
| ac564000b1 | |||
| 1dad987073 | |||
| 6728abf595 | |||
| 669fce36e9 | |||
| b11003c472 | |||
| 187f80bfc9 | |||
| 611d216bb0 | |||
| 8a1cd32d50 | |||
| 2a18b27114 | |||
| 55f91143d9 | |||
| cf564a7d06 | |||
| b55067ad60 | |||
| a437c698eb | |||
| f8ada70fe7 | |||
| 196d9db3fa | |||
| e29eaf3785 | |||
| 7048ea473f | |||
| 720b1eeb27 | |||
| 4e282dac5b | |||
| 42343254b1 | |||
| e98829c96b | |||
| 5791f12a28 | |||
| fe9a6e8fdd | |||
| dd3f4e4751 | |||
| a89fa766ea | |||
| 39c2cf6c72 | |||
| c1ce72469c | |||
| eca76dd7c4 | |||
| 621b2d8fc9 | |||
| 4039b962b4 | |||
| 70cc0b93f0 | |||
| 81e90cd4e4 | |||
| 80bd3be0a1 | |||
| 829a794df7 | |||
| 00997408b8 | |||
| 4d3bdfc52f | |||
| b14d0926ed | |||
| 59b46db8db | |||
| a240284727 | |||
| 3d314324e0 | |||
| b9829bb696 | |||
| 85f03fb1d0 | |||
| 69810303e5 | |||
| 5b0e31acf3 | |||
| f0a934e014 | |||
| 328e4db1a2 | |||
| a708265e14 | |||
| a0618f7f47 | |||
| f2bd9fae01 | |||
| 64f717f81b | |||
| fd17377c18 | |||
| 6698fdb6e6 | |||
| e2eab1ee5d | |||
| aee9bf7cc2 | |||
| 63d9d554b9 | |||
| f6c04f9e74 | |||
| e167300194 | |||
| 319400ab13 | |||
| 38b1717fac | |||
| d5938a016c | |||
| 82d6103907 | |||
| b353db67d4 | |||
| 23be3e5ba3 | |||
| 34ba762b42 |
Generated
+275
-49
@@ -2243,6 +2243,10 @@
|
||||
"resolved": "packages/serial",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@strudel.cycles/soundfonts": {
|
||||
"resolved": "packages/soundfonts",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@strudel.cycles/tonal": {
|
||||
"resolved": "packages/tonal",
|
||||
"link": true
|
||||
@@ -3947,6 +3951,15 @@
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/data-uri-to-buffer": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz",
|
||||
"integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/dateformat": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
|
||||
@@ -4955,6 +4968,29 @@
|
||||
"reusify": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/fetch-blob": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.5.tgz",
|
||||
"integrity": "sha512-N64ZpKqoLejlrwkIAnb9iLSA3Vx/kjgzpcDhygcqJ2KKjky8nCgUQ+dzXtbrLaWZGZNmNfQTsiQ0weZ1svglHg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "paypal",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"node-domexception": "^1.0.0",
|
||||
"web-streams-polyfill": "^3.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20 || >= 14.13"
|
||||
}
|
||||
},
|
||||
"node_modules/figures": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
|
||||
@@ -5134,6 +5170,18 @@
|
||||
"node": ">= 0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/formdata-polyfill": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fetch-blob": "^3.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fraction.js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
|
||||
@@ -7019,9 +7067,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-4.0.14.tgz",
|
||||
"integrity": "sha512-HL5sSPE/LP6U9qKgngIIPTthuxC0jrfxpYMZ3LdGDD3vTnLs59m2Z7r6+LNDR3ToqEQdkKd6YaaEfJhodJmijQ==",
|
||||
"version": "4.0.17",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-4.0.17.tgz",
|
||||
"integrity": "sha512-Wfk0ATOK5iPxM4ptrORkFemqroz0ZDxp5MWfYA7H/F+wO17NRWV5Ypxi6p3g2Xmw2bKeiYOl6oVnLHKxBA0VhA==",
|
||||
"bin": {
|
||||
"marked": "bin/marked.js"
|
||||
},
|
||||
@@ -7582,6 +7630,25 @@
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/node-domexception": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
@@ -8437,6 +8504,31 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/peggy": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/peggy/-/peggy-2.0.1.tgz",
|
||||
"integrity": "sha512-mBqfmdUAOVn7RILpXTbcRBhLfTR4Go0SresSnivGDdRylBOyVFJncFiVyCNNpPWq8HmgeRleXHs/Go4o8kQVXA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"commander": "^9.3.0",
|
||||
"source-map-generator": "0.8.0"
|
||||
},
|
||||
"bin": {
|
||||
"peggy": "bin/peggy.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/peggy/node_modules/commander": {
|
||||
"version": "9.3.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-9.3.0.tgz",
|
||||
"integrity": "sha512-hv95iU5uXPbK83mjrJKuZyFM/LBAoCV/XhVGkS5Je6tl7sxr6A0ITMw5WoRV46/UaJ46Nllm3Xt7IaJhXTIkzw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.20.0 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/performance-now": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
@@ -9834,6 +9926,15 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-generator": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map-generator/-/source-map-generator-0.8.0.tgz",
|
||||
"integrity": "sha512-psgxdGMwl5MZM9S3FWee4EgsEaIjahYV5AzGnwUvPhWeITz/j6rKpysQHlQ4USdxvINlb8lKfWGIXwfkrgtqkA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
@@ -10819,6 +10920,15 @@
|
||||
"defaults": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
|
||||
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/WebDirt": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "git+ssh://git@github.com/dktr0/WebDirt.git#425dc8fd023440d9c61ffdb8642e44e2710faea0",
|
||||
@@ -11263,7 +11373,7 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@strudel.cycles/core",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.2",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"bjork": "^0.0.1",
|
||||
@@ -11275,15 +11385,15 @@
|
||||
},
|
||||
"packages/embed": {
|
||||
"name": "@strudel.cycles/embed",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"license": "AGPL-3.0-or-later"
|
||||
},
|
||||
"packages/eval": {
|
||||
"name": "@strudel.cycles/eval",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.3",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.1.0",
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"estraverse": "^5.3.0",
|
||||
"shift-ast": "^6.1.0",
|
||||
"shift-codegen": "^7.0.3",
|
||||
@@ -11307,27 +11417,30 @@
|
||||
},
|
||||
"packages/midi": {
|
||||
"name": "@strudel.cycles/midi",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.3",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/tone": "^0.1.1",
|
||||
"@strudel.cycles/tone": "^0.1.3",
|
||||
"tone": "^14.7.77",
|
||||
"webmidi": "^2.5.2"
|
||||
}
|
||||
},
|
||||
"packages/mini": {
|
||||
"name": "@strudel.cycles/mini",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.3",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.1.0",
|
||||
"@strudel.cycles/eval": "^0.1.1",
|
||||
"@strudel.cycles/tone": "^0.1.1"
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"@strudel.cycles/eval": "^0.1.3",
|
||||
"@strudel.cycles/tone": "^0.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"peggy": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"packages/osc": {
|
||||
"name": "@strudel.cycles/osc",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"osc-js": "^2.3.2"
|
||||
@@ -11335,14 +11448,14 @@
|
||||
},
|
||||
"packages/react": {
|
||||
"name": "@strudel.cycles/react",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.4",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@codemirror/lang-javascript": "^0.19.0",
|
||||
"@strudel.cycles/core": "*",
|
||||
"@strudel.cycles/eval": "^0.1.1",
|
||||
"@strudel.cycles/tone": "^0.1.1",
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"@strudel.cycles/eval": "^0.1.3",
|
||||
"@strudel.cycles/tone": "^0.1.3",
|
||||
"react-codemirror6": "^1.1.0",
|
||||
"react-hook-inview": "^4.5.0"
|
||||
},
|
||||
@@ -11411,15 +11524,45 @@
|
||||
},
|
||||
"packages/serial": {
|
||||
"name": "@strudel.cycles/serial",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.3",
|
||||
"license": "AGPL-3.0-or-later"
|
||||
},
|
||||
"packages/tonal": {
|
||||
"name": "@strudel.cycles/tonal",
|
||||
"packages/soundfonts": {
|
||||
"name": "@strudel.cycles/soundfonts",
|
||||
"version": "0.1.1",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.1.0",
|
||||
"@strudel.cycles/core": "*",
|
||||
"@strudel.cycles/webaudio": "^0.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"node-fetch": "^3.2.6"
|
||||
}
|
||||
},
|
||||
"packages/soundfonts/node_modules/node-fetch": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.6.tgz",
|
||||
"integrity": "sha512-LAy/HZnLADOVkVPubaxHDft29booGglPFDr2Hw0J1AercRh01UiVFm++KMDnJeH9sHgNB4hsXPii7Sgym/sTbw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"data-uri-to-buffer": "^4.0.0",
|
||||
"fetch-blob": "^3.1.4",
|
||||
"formdata-polyfill": "^4.0.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/node-fetch"
|
||||
}
|
||||
},
|
||||
"packages/tonal": {
|
||||
"name": "@strudel.cycles/tonal",
|
||||
"version": "0.1.3",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"@tonaljs/tonal": "^4.6.5",
|
||||
"webmidi": "^3.0.15"
|
||||
}
|
||||
@@ -11441,10 +11584,10 @@
|
||||
},
|
||||
"packages/tone": {
|
||||
"name": "@strudel.cycles/tone",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.3",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.1.0",
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"@tonejs/piano": "^0.2.1",
|
||||
"chord-voicings": "^0.0.1",
|
||||
"tone": "^14.7.77"
|
||||
@@ -11452,27 +11595,27 @@
|
||||
},
|
||||
"packages/webaudio": {
|
||||
"name": "@strudel.cycles/webaudio",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.4",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.1.0"
|
||||
"@strudel.cycles/core": "^0.1.2"
|
||||
}
|
||||
},
|
||||
"packages/webdirt": {
|
||||
"name": "@strudel.cycles/webdirt",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.2",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.1.0",
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"WebDirt": "github:dktr0/WebDirt"
|
||||
}
|
||||
},
|
||||
"packages/xen": {
|
||||
"name": "@strudel.cycles/xen",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.3",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.1.0"
|
||||
"@strudel.cycles/core": "^0.1.2"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -13287,7 +13430,7 @@
|
||||
"@strudel.cycles/eval": {
|
||||
"version": "file:packages/eval",
|
||||
"requires": {
|
||||
"@strudel.cycles/core": "^0.1.0",
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"estraverse": "^5.3.0",
|
||||
"shift-ast": "^6.1.0",
|
||||
"shift-codegen": "^7.0.3",
|
||||
@@ -13299,7 +13442,7 @@
|
||||
"@strudel.cycles/midi": {
|
||||
"version": "file:packages/midi",
|
||||
"requires": {
|
||||
"@strudel.cycles/tone": "^0.1.1",
|
||||
"@strudel.cycles/tone": "^0.1.3",
|
||||
"tone": "^14.7.77",
|
||||
"webmidi": "^2.5.2"
|
||||
}
|
||||
@@ -13307,9 +13450,10 @@
|
||||
"@strudel.cycles/mini": {
|
||||
"version": "file:packages/mini",
|
||||
"requires": {
|
||||
"@strudel.cycles/core": "^0.1.0",
|
||||
"@strudel.cycles/eval": "^0.1.1",
|
||||
"@strudel.cycles/tone": "^0.1.1"
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"@strudel.cycles/eval": "^0.1.3",
|
||||
"@strudel.cycles/tone": "^0.1.3",
|
||||
"peggy": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"@strudel.cycles/osc": {
|
||||
@@ -13322,9 +13466,9 @@
|
||||
"version": "file:packages/react",
|
||||
"requires": {
|
||||
"@codemirror/lang-javascript": "^0.19.0",
|
||||
"@strudel.cycles/core": "*",
|
||||
"@strudel.cycles/eval": "^0.1.1",
|
||||
"@strudel.cycles/tone": "^0.1.1",
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"@strudel.cycles/eval": "^0.1.3",
|
||||
"@strudel.cycles/tone": "^0.1.3",
|
||||
"@types/react": "^17.0.2",
|
||||
"@types/react-dom": "^17.0.2",
|
||||
"@vitejs/plugin-react": "^1.3.0",
|
||||
@@ -13384,10 +13528,31 @@
|
||||
"@strudel.cycles/serial": {
|
||||
"version": "file:packages/serial"
|
||||
},
|
||||
"@strudel.cycles/soundfonts": {
|
||||
"version": "file:packages/soundfonts",
|
||||
"requires": {
|
||||
"@strudel.cycles/core": "*",
|
||||
"@strudel.cycles/webaudio": "^0.1.4",
|
||||
"node-fetch": "^3.2.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-fetch": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.6.tgz",
|
||||
"integrity": "sha512-LAy/HZnLADOVkVPubaxHDft29booGglPFDr2Hw0J1AercRh01UiVFm++KMDnJeH9sHgNB4hsXPii7Sgym/sTbw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"data-uri-to-buffer": "^4.0.0",
|
||||
"fetch-blob": "^3.1.4",
|
||||
"formdata-polyfill": "^4.0.10"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@strudel.cycles/tonal": {
|
||||
"version": "file:packages/tonal",
|
||||
"requires": {
|
||||
"@strudel.cycles/core": "^0.1.0",
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"@tonaljs/tonal": "^4.6.5",
|
||||
"webmidi": "^3.0.15"
|
||||
},
|
||||
@@ -13407,7 +13572,7 @@
|
||||
"@strudel.cycles/tone": {
|
||||
"version": "file:packages/tone",
|
||||
"requires": {
|
||||
"@strudel.cycles/core": "^0.1.0",
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"@tonejs/piano": "^0.2.1",
|
||||
"chord-voicings": "^0.0.1",
|
||||
"tone": "^14.7.77"
|
||||
@@ -13416,20 +13581,20 @@
|
||||
"@strudel.cycles/webaudio": {
|
||||
"version": "file:packages/webaudio",
|
||||
"requires": {
|
||||
"@strudel.cycles/core": "^0.1.0"
|
||||
"@strudel.cycles/core": "^0.1.2"
|
||||
}
|
||||
},
|
||||
"@strudel.cycles/webdirt": {
|
||||
"version": "file:packages/webdirt",
|
||||
"requires": {
|
||||
"@strudel.cycles/core": "^0.1.0",
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"WebDirt": "github:dktr0/WebDirt"
|
||||
}
|
||||
},
|
||||
"@strudel.cycles/xen": {
|
||||
"version": "file:packages/xen",
|
||||
"requires": {
|
||||
"@strudel.cycles/core": "^0.1.0"
|
||||
"@strudel.cycles/core": "^0.1.2"
|
||||
}
|
||||
},
|
||||
"@tonaljs/abc-notation": {
|
||||
@@ -14808,6 +14973,12 @@
|
||||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"data-uri-to-buffer": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz",
|
||||
"integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==",
|
||||
"dev": true
|
||||
},
|
||||
"dateformat": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
|
||||
@@ -15481,6 +15652,16 @@
|
||||
"reusify": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"fetch-blob": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.5.tgz",
|
||||
"integrity": "sha512-N64ZpKqoLejlrwkIAnb9iLSA3Vx/kjgzpcDhygcqJ2KKjky8nCgUQ+dzXtbrLaWZGZNmNfQTsiQ0weZ1svglHg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"node-domexception": "^1.0.0",
|
||||
"web-streams-polyfill": "^3.0.3"
|
||||
}
|
||||
},
|
||||
"figures": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
|
||||
@@ -15609,6 +15790,15 @@
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"formdata-polyfill": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fetch-blob": "^3.1.2"
|
||||
}
|
||||
},
|
||||
"fraction.js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
|
||||
@@ -17058,9 +17248,9 @@
|
||||
"requires": {}
|
||||
},
|
||||
"marked": {
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-4.0.14.tgz",
|
||||
"integrity": "sha512-HL5sSPE/LP6U9qKgngIIPTthuxC0jrfxpYMZ3LdGDD3vTnLs59m2Z7r6+LNDR3ToqEQdkKd6YaaEfJhodJmijQ=="
|
||||
"version": "4.0.17",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-4.0.17.tgz",
|
||||
"integrity": "sha512-Wfk0ATOK5iPxM4ptrORkFemqroz0ZDxp5MWfYA7H/F+wO17NRWV5Ypxi6p3g2Xmw2bKeiYOl6oVnLHKxBA0VhA=="
|
||||
},
|
||||
"mdurl": {
|
||||
"version": "1.0.1",
|
||||
@@ -17485,6 +17675,12 @@
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||
"dev": true
|
||||
},
|
||||
"node-domexception": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
@@ -18134,6 +18330,24 @@
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"dev": true
|
||||
},
|
||||
"peggy": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/peggy/-/peggy-2.0.1.tgz",
|
||||
"integrity": "sha512-mBqfmdUAOVn7RILpXTbcRBhLfTR4Go0SresSnivGDdRylBOyVFJncFiVyCNNpPWq8HmgeRleXHs/Go4o8kQVXA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commander": "^9.3.0",
|
||||
"source-map-generator": "0.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "9.3.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-9.3.0.tgz",
|
||||
"integrity": "sha512-hv95iU5uXPbK83mjrJKuZyFM/LBAoCV/XhVGkS5Je6tl7sxr6A0ITMw5WoRV46/UaJ46Nllm3Xt7IaJhXTIkzw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"performance-now": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
@@ -18386,9 +18600,9 @@
|
||||
"version": "file:packages/react",
|
||||
"requires": {
|
||||
"@codemirror/lang-javascript": "^0.19.0",
|
||||
"@strudel.cycles/core": "*",
|
||||
"@strudel.cycles/eval": "^0.1.1",
|
||||
"@strudel.cycles/tone": "^0.1.1",
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"@strudel.cycles/eval": "^0.1.3",
|
||||
"@strudel.cycles/tone": "^0.1.3",
|
||||
"@types/react": "^17.0.2",
|
||||
"@types/react-dom": "^17.0.2",
|
||||
"@vitejs/plugin-react": "^1.3.0",
|
||||
@@ -19252,6 +19466,12 @@
|
||||
"is-plain-obj": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"source-map-generator": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map-generator/-/source-map-generator-0.8.0.tgz",
|
||||
"integrity": "sha512-psgxdGMwl5MZM9S3FWee4EgsEaIjahYV5AzGnwUvPhWeITz/j6rKpysQHlQ4USdxvINlb8lKfWGIXwfkrgtqkA==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
@@ -20029,6 +20249,12 @@
|
||||
"defaults": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"web-streams-polyfill": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
|
||||
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
|
||||
"dev": true
|
||||
},
|
||||
"WebDirt": {
|
||||
"version": "git+ssh://git@github.com/dktr0/WebDirt.git#425dc8fd023440d9c61ffdb8642e44e2710faea0",
|
||||
"from": "WebDirt@github:dktr0/WebDirt"
|
||||
|
||||
@@ -744,6 +744,7 @@ const generic_params = [
|
||||
['f', 'uid', ''],
|
||||
['f', 'val', ''],
|
||||
['f', 'cps', ''],
|
||||
['f', 'clip', ''],
|
||||
];
|
||||
|
||||
// TODO: slice / splice https://www.youtube.com/watch?v=hKhPdO0RKDQ&list=PL2lW1zNIIwj3bDkh-Y3LUGDuRcoUigoDs&index=13
|
||||
@@ -765,7 +766,8 @@ generic_params.forEach(([type, name, description]) => {
|
||||
|
||||
// create custom param
|
||||
controls.createParam = (name) => {
|
||||
Pattern.prototype[name] = _setter(controls[name], name);
|
||||
const func = (...pats) => _name(name, ...pats);
|
||||
Pattern.prototype[name] = _setter(func, name);
|
||||
return (...pats) => _name(name, ...pats);
|
||||
};
|
||||
|
||||
|
||||
+15
-1
@@ -16,4 +16,18 @@ export * from './timespan.mjs';
|
||||
export * from './util.mjs';
|
||||
export * from './speak.mjs';
|
||||
export { default as gist } from './gist.js';
|
||||
// export * from './value.mjs';
|
||||
// below won't work with runtime.mjs (json import fails)
|
||||
/* import * as p from './package.json';
|
||||
export const version = p.version; */
|
||||
console.log(
|
||||
'%c // 🌀 @strudel.cycles/core loaded 🌀', // keep "//" for runnable snapshot source..
|
||||
'background-color: black;color:white;padding:4px;border-radius:15px',
|
||||
);
|
||||
if (globalThis._strudelLoaded) {
|
||||
console.warn(
|
||||
`@strudel.cycles/core was loaded more than once...
|
||||
This might happen when you have multiple versions of strudel installed.
|
||||
Please check with "npm ls @strudel.cycles/core".`,
|
||||
);
|
||||
}
|
||||
globalThis._strudelLoaded = true;
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/core",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/core",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"description": "Port of Tidal Cycles to JavaScript",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
|
||||
@@ -15,6 +15,7 @@ import drawLine from './drawLine.mjs';
|
||||
|
||||
/** @class Class representing a pattern. */
|
||||
export class Pattern {
|
||||
_Pattern = true; // this property is used to detect if a pattern that fails instanceof Pattern is an instance of another Pattern
|
||||
/**
|
||||
* Create a pattern. As an end user, you will most likely not create a Pattern directly.
|
||||
*
|
||||
@@ -1212,7 +1213,15 @@ export function pure(value) {
|
||||
|
||||
export function isPattern(thing) {
|
||||
// thing?.constructor?.name !== 'Pattern' // <- this will fail when code is mangled
|
||||
return thing instanceof Pattern;
|
||||
const is = thing instanceof Pattern || thing._Pattern;
|
||||
if (!thing instanceof Pattern) {
|
||||
console.warn(
|
||||
`Found Pattern that fails "instanceof Pattern" check.
|
||||
This may happen if you are using multiple versions of @strudel.cycles/core.
|
||||
Please check by running "npm ls @strudel.cycles/core".`,
|
||||
);
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
export function reify(thing) {
|
||||
@@ -1389,7 +1398,7 @@ export function pr(args) {
|
||||
}
|
||||
|
||||
export const add = curry((a, pat) => pat.add(a));
|
||||
export const chop = curry((a, pat) => pat.chop(a))
|
||||
export const chop = curry((a, pat) => pat.chop(a));
|
||||
export const chunk = curry((a, pat) => pat.chunk(a));
|
||||
export const chunkBack = curry((a, pat) => pat.chunkBack(a));
|
||||
export const div = curry((a, pat) => pat.div(a));
|
||||
|
||||
@@ -10,7 +10,7 @@ export const tokenizeNote = (note) => {
|
||||
if (typeof note !== 'string') {
|
||||
return [];
|
||||
}
|
||||
const [pc, acc = '', oct] = note.match(/^([a-gA-G])([#b]*)([0-9])?$/)?.slice(1) || [];
|
||||
const [pc, acc = '', oct] = note.match(/^([a-gA-G])([#bs]*)([0-9])?$/)?.slice(1) || [];
|
||||
if (!pc) {
|
||||
return [];
|
||||
}
|
||||
@@ -24,7 +24,7 @@ export const toMidi = (note) => {
|
||||
throw new Error('not a note: "' + note + '"');
|
||||
}
|
||||
const chroma = { c: 0, d: 2, e: 4, f: 5, g: 7, a: 9, b: 11 }[pc.toLowerCase()];
|
||||
const offset = acc?.split('').reduce((o, char) => o + { '#': 1, b: -1 }[char], 0) || 0;
|
||||
const offset = acc?.split('').reduce((o, char) => o + { '#': 1, b: -1, s: 1 }[char], 0) || 0;
|
||||
return (Number(oct) + 1) * 12 + chroma + offset;
|
||||
};
|
||||
export const fromMidi = (n) => {
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/eval",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/eval",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"description": "Code evaluator for strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
@@ -28,7 +28,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.1.1",
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"estraverse": "^5.3.0",
|
||||
"shift-ast": "^6.1.0",
|
||||
"shift-codegen": "^7.0.3",
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/midi",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/midi",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"description": "Midi API for strudel",
|
||||
"main": "index.mjs",
|
||||
"repository": {
|
||||
@@ -21,7 +21,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/tone": "^0.1.2",
|
||||
"@strudel.cycles/tone": "^0.1.3",
|
||||
"tone": "^14.7.77",
|
||||
"webmidi": "^2.5.2"
|
||||
}
|
||||
|
||||
+164
-158
@@ -1,10 +1,4 @@
|
||||
/*
|
||||
krill-parser.js - <short description TODO>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/mini/krill-parser.js>
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Generated by Peggy 1.2.0.
|
||||
// Generated by Peggy 2.0.1.
|
||||
//
|
||||
// https://peggyjs.org/
|
||||
|
||||
@@ -16,6 +10,7 @@ function peg$subclass(child, parent) {
|
||||
|
||||
function peg$SyntaxError(message, expected, found, location) {
|
||||
var self = Error.call(this, message);
|
||||
// istanbul ignore next Check is a necessary evil to support older environments
|
||||
if (Object.setPrototypeOf) {
|
||||
Object.setPrototypeOf(self, peg$SyntaxError.prototype);
|
||||
}
|
||||
@@ -51,14 +46,15 @@ peg$SyntaxError.prototype.format = function(sources) {
|
||||
var loc = this.location.source + ":" + s.line + ":" + s.column;
|
||||
if (src) {
|
||||
var e = this.location.end;
|
||||
var filler = peg$padEnd("", s.line.toString().length);
|
||||
var filler = peg$padEnd("", s.line.toString().length, ' ');
|
||||
var line = src[s.line - 1];
|
||||
var last = s.line === e.line ? e.column : line.length + 1;
|
||||
var hatLen = (last - s.column) || 1;
|
||||
str += "\n --> " + loc + "\n"
|
||||
+ filler + " |\n"
|
||||
+ s.line + " | " + line + "\n"
|
||||
+ filler + " | " + peg$padEnd("", s.column - 1)
|
||||
+ peg$padEnd("", last - s.column, "^");
|
||||
+ filler + " | " + peg$padEnd("", s.column - 1, ' ')
|
||||
+ peg$padEnd("", hatLen, "^");
|
||||
} else {
|
||||
str += "\n at " + loc;
|
||||
}
|
||||
@@ -79,7 +75,7 @@ peg$SyntaxError.buildMessage = function(expected, found) {
|
||||
: classEscape(part);
|
||||
});
|
||||
|
||||
return "[" + (expectation.inverted ? "^" : "") + escapedParts + "]";
|
||||
return "[" + (expectation.inverted ? "^" : "") + escapedParts.join("") + "]";
|
||||
},
|
||||
|
||||
any: function() {
|
||||
@@ -185,31 +181,32 @@ function peg$parse(input, options) {
|
||||
var peg$c7 = "#";
|
||||
var peg$c8 = "^";
|
||||
var peg$c9 = "_";
|
||||
var peg$c10 = "[";
|
||||
var peg$c11 = "]";
|
||||
var peg$c12 = "<";
|
||||
var peg$c13 = ">";
|
||||
var peg$c14 = "@";
|
||||
var peg$c15 = "!";
|
||||
var peg$c16 = "(";
|
||||
var peg$c17 = ")";
|
||||
var peg$c18 = "/";
|
||||
var peg$c19 = "*";
|
||||
var peg$c20 = "%";
|
||||
var peg$c21 = "struct";
|
||||
var peg$c22 = "target";
|
||||
var peg$c23 = "euclid";
|
||||
var peg$c24 = "slow";
|
||||
var peg$c25 = "rotL";
|
||||
var peg$c26 = "rotR";
|
||||
var peg$c27 = "fast";
|
||||
var peg$c28 = "scale";
|
||||
var peg$c29 = "//";
|
||||
var peg$c30 = "cat";
|
||||
var peg$c31 = "$";
|
||||
var peg$c32 = "setcps";
|
||||
var peg$c33 = "setbpm";
|
||||
var peg$c34 = "hush";
|
||||
var peg$c10 = ":";
|
||||
var peg$c11 = "[";
|
||||
var peg$c12 = "]";
|
||||
var peg$c13 = "<";
|
||||
var peg$c14 = ">";
|
||||
var peg$c15 = "@";
|
||||
var peg$c16 = "!";
|
||||
var peg$c17 = "(";
|
||||
var peg$c18 = ")";
|
||||
var peg$c19 = "/";
|
||||
var peg$c20 = "*";
|
||||
var peg$c21 = "%";
|
||||
var peg$c22 = "struct";
|
||||
var peg$c23 = "target";
|
||||
var peg$c24 = "euclid";
|
||||
var peg$c25 = "slow";
|
||||
var peg$c26 = "rotL";
|
||||
var peg$c27 = "rotR";
|
||||
var peg$c28 = "fast";
|
||||
var peg$c29 = "scale";
|
||||
var peg$c30 = "//";
|
||||
var peg$c31 = "cat";
|
||||
var peg$c32 = "$";
|
||||
var peg$c33 = "setcps";
|
||||
var peg$c34 = "setbpm";
|
||||
var peg$c35 = "hush";
|
||||
|
||||
var peg$r0 = /^[1-9]/;
|
||||
var peg$r1 = /^[eE]/;
|
||||
@@ -235,32 +232,33 @@ function peg$parse(input, options) {
|
||||
var peg$e14 = peg$literalExpectation("#", false);
|
||||
var peg$e15 = peg$literalExpectation("^", false);
|
||||
var peg$e16 = peg$literalExpectation("_", false);
|
||||
var peg$e17 = peg$literalExpectation("[", false);
|
||||
var peg$e18 = peg$literalExpectation("]", false);
|
||||
var peg$e19 = peg$literalExpectation("<", false);
|
||||
var peg$e20 = peg$literalExpectation(">", false);
|
||||
var peg$e21 = peg$literalExpectation("@", false);
|
||||
var peg$e22 = peg$literalExpectation("!", false);
|
||||
var peg$e23 = peg$literalExpectation("(", false);
|
||||
var peg$e24 = peg$literalExpectation(")", false);
|
||||
var peg$e25 = peg$literalExpectation("/", false);
|
||||
var peg$e26 = peg$literalExpectation("*", false);
|
||||
var peg$e27 = peg$literalExpectation("%", false);
|
||||
var peg$e28 = peg$literalExpectation("struct", false);
|
||||
var peg$e29 = peg$literalExpectation("target", false);
|
||||
var peg$e30 = peg$literalExpectation("euclid", false);
|
||||
var peg$e31 = peg$literalExpectation("slow", false);
|
||||
var peg$e32 = peg$literalExpectation("rotL", false);
|
||||
var peg$e33 = peg$literalExpectation("rotR", false);
|
||||
var peg$e34 = peg$literalExpectation("fast", false);
|
||||
var peg$e35 = peg$literalExpectation("scale", false);
|
||||
var peg$e36 = peg$literalExpectation("//", false);
|
||||
var peg$e37 = peg$classExpectation(["\n"], true, false);
|
||||
var peg$e38 = peg$literalExpectation("cat", false);
|
||||
var peg$e39 = peg$literalExpectation("$", false);
|
||||
var peg$e40 = peg$literalExpectation("setcps", false);
|
||||
var peg$e41 = peg$literalExpectation("setbpm", false);
|
||||
var peg$e42 = peg$literalExpectation("hush", false);
|
||||
var peg$e17 = peg$literalExpectation(":", false);
|
||||
var peg$e18 = peg$literalExpectation("[", false);
|
||||
var peg$e19 = peg$literalExpectation("]", false);
|
||||
var peg$e20 = peg$literalExpectation("<", false);
|
||||
var peg$e21 = peg$literalExpectation(">", false);
|
||||
var peg$e22 = peg$literalExpectation("@", false);
|
||||
var peg$e23 = peg$literalExpectation("!", false);
|
||||
var peg$e24 = peg$literalExpectation("(", false);
|
||||
var peg$e25 = peg$literalExpectation(")", false);
|
||||
var peg$e26 = peg$literalExpectation("/", false);
|
||||
var peg$e27 = peg$literalExpectation("*", false);
|
||||
var peg$e28 = peg$literalExpectation("%", false);
|
||||
var peg$e29 = peg$literalExpectation("struct", false);
|
||||
var peg$e30 = peg$literalExpectation("target", false);
|
||||
var peg$e31 = peg$literalExpectation("euclid", false);
|
||||
var peg$e32 = peg$literalExpectation("slow", false);
|
||||
var peg$e33 = peg$literalExpectation("rotL", false);
|
||||
var peg$e34 = peg$literalExpectation("rotR", false);
|
||||
var peg$e35 = peg$literalExpectation("fast", false);
|
||||
var peg$e36 = peg$literalExpectation("scale", false);
|
||||
var peg$e37 = peg$literalExpectation("//", false);
|
||||
var peg$e38 = peg$classExpectation(["\n"], true, false);
|
||||
var peg$e39 = peg$literalExpectation("cat", false);
|
||||
var peg$e40 = peg$literalExpectation("$", false);
|
||||
var peg$e41 = peg$literalExpectation("setcps", false);
|
||||
var peg$e42 = peg$literalExpectation("setbpm", false);
|
||||
var peg$e43 = peg$literalExpectation("hush", false);
|
||||
|
||||
var peg$f0 = function() { return parseFloat(text()); };
|
||||
var peg$f1 = function(chars) { return chars.join("") };
|
||||
@@ -294,7 +292,6 @@ function peg$parse(input, options) {
|
||||
var peg$f29 = function(v) { return new CommandStub("setcps", { value: v})};
|
||||
var peg$f30 = function(v) { return new CommandStub("setcps", { value: (v/120/2)})};
|
||||
var peg$f31 = function() { return new CommandStub("hush")};
|
||||
|
||||
var peg$currPos = 0;
|
||||
var peg$savedPos = 0;
|
||||
var peg$posDetailsCache = [{ line: 1, column: 1 }];
|
||||
@@ -810,6 +807,15 @@ function peg$parse(input, options) {
|
||||
s0 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e16); }
|
||||
}
|
||||
if (s0 === peg$FAILED) {
|
||||
if (input.charCodeAt(peg$currPos) === 58) {
|
||||
s0 = peg$c10;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s0 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e17); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -852,11 +858,11 @@ function peg$parse(input, options) {
|
||||
s0 = peg$currPos;
|
||||
s1 = peg$parsews();
|
||||
if (input.charCodeAt(peg$currPos) === 91) {
|
||||
s2 = peg$c10;
|
||||
s2 = peg$c11;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s2 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e17); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e18); }
|
||||
}
|
||||
if (s2 !== peg$FAILED) {
|
||||
s3 = peg$parsews();
|
||||
@@ -864,11 +870,11 @@ function peg$parse(input, options) {
|
||||
if (s4 !== peg$FAILED) {
|
||||
s5 = peg$parsews();
|
||||
if (input.charCodeAt(peg$currPos) === 93) {
|
||||
s6 = peg$c11;
|
||||
s6 = peg$c12;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s6 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e18); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e19); }
|
||||
}
|
||||
if (s6 !== peg$FAILED) {
|
||||
s7 = peg$parsews();
|
||||
@@ -896,11 +902,11 @@ function peg$parse(input, options) {
|
||||
s0 = peg$currPos;
|
||||
s1 = peg$parsews();
|
||||
if (input.charCodeAt(peg$currPos) === 60) {
|
||||
s2 = peg$c12;
|
||||
s2 = peg$c13;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s2 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e19); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e20); }
|
||||
}
|
||||
if (s2 !== peg$FAILED) {
|
||||
s3 = peg$parsews();
|
||||
@@ -908,11 +914,11 @@ function peg$parse(input, options) {
|
||||
if (s4 !== peg$FAILED) {
|
||||
s5 = peg$parsews();
|
||||
if (input.charCodeAt(peg$currPos) === 62) {
|
||||
s6 = peg$c13;
|
||||
s6 = peg$c14;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s6 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e20); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e21); }
|
||||
}
|
||||
if (s6 !== peg$FAILED) {
|
||||
s7 = peg$parsews();
|
||||
@@ -976,11 +982,11 @@ function peg$parse(input, options) {
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.charCodeAt(peg$currPos) === 64) {
|
||||
s1 = peg$c14;
|
||||
s1 = peg$c15;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e21); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e22); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsenumber();
|
||||
@@ -1004,11 +1010,11 @@ function peg$parse(input, options) {
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.charCodeAt(peg$currPos) === 33) {
|
||||
s1 = peg$c15;
|
||||
s1 = peg$c16;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e22); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e23); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsenumber();
|
||||
@@ -1032,11 +1038,11 @@ function peg$parse(input, options) {
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.charCodeAt(peg$currPos) === 40) {
|
||||
s1 = peg$c16;
|
||||
s1 = peg$c17;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e23); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e24); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsews();
|
||||
@@ -1060,11 +1066,11 @@ function peg$parse(input, options) {
|
||||
}
|
||||
s12 = peg$parsews();
|
||||
if (input.charCodeAt(peg$currPos) === 41) {
|
||||
s13 = peg$c17;
|
||||
s13 = peg$c18;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s13 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e24); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e25); }
|
||||
}
|
||||
if (s13 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
@@ -1098,11 +1104,11 @@ function peg$parse(input, options) {
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.charCodeAt(peg$currPos) === 47) {
|
||||
s1 = peg$c18;
|
||||
s1 = peg$c19;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e25); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e26); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsenumber();
|
||||
@@ -1126,11 +1132,11 @@ function peg$parse(input, options) {
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.charCodeAt(peg$currPos) === 42) {
|
||||
s1 = peg$c19;
|
||||
s1 = peg$c20;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e26); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e27); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsenumber();
|
||||
@@ -1154,11 +1160,11 @@ function peg$parse(input, options) {
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.charCodeAt(peg$currPos) === 37) {
|
||||
s1 = peg$c20;
|
||||
s1 = peg$c21;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e27); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e28); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsenumber();
|
||||
@@ -1332,12 +1338,12 @@ function peg$parse(input, options) {
|
||||
var s0, s1, s2, s3;
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 6) === peg$c21) {
|
||||
s1 = peg$c21;
|
||||
if (input.substr(peg$currPos, 6) === peg$c22) {
|
||||
s1 = peg$c22;
|
||||
peg$currPos += 6;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e28); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e29); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsews();
|
||||
@@ -1361,12 +1367,12 @@ function peg$parse(input, options) {
|
||||
var s0, s1, s2, s3, s4, s5;
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 6) === peg$c22) {
|
||||
s1 = peg$c22;
|
||||
if (input.substr(peg$currPos, 6) === peg$c23) {
|
||||
s1 = peg$c23;
|
||||
peg$currPos += 6;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e29); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e30); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsews();
|
||||
@@ -1402,12 +1408,12 @@ function peg$parse(input, options) {
|
||||
var s0, s1, s2, s3, s4, s5, s6, s7;
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 6) === peg$c23) {
|
||||
s1 = peg$c23;
|
||||
if (input.substr(peg$currPos, 6) === peg$c24) {
|
||||
s1 = peg$c24;
|
||||
peg$currPos += 6;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e30); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e31); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsews();
|
||||
@@ -1443,12 +1449,12 @@ function peg$parse(input, options) {
|
||||
var s0, s1, s2, s3;
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 4) === peg$c24) {
|
||||
s1 = peg$c24;
|
||||
if (input.substr(peg$currPos, 4) === peg$c25) {
|
||||
s1 = peg$c25;
|
||||
peg$currPos += 4;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e31); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e32); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsews();
|
||||
@@ -1472,12 +1478,12 @@ function peg$parse(input, options) {
|
||||
var s0, s1, s2, s3;
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 4) === peg$c25) {
|
||||
s1 = peg$c25;
|
||||
if (input.substr(peg$currPos, 4) === peg$c26) {
|
||||
s1 = peg$c26;
|
||||
peg$currPos += 4;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e32); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e33); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsews();
|
||||
@@ -1501,12 +1507,12 @@ function peg$parse(input, options) {
|
||||
var s0, s1, s2, s3;
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 4) === peg$c26) {
|
||||
s1 = peg$c26;
|
||||
if (input.substr(peg$currPos, 4) === peg$c27) {
|
||||
s1 = peg$c27;
|
||||
peg$currPos += 4;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e33); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e34); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsews();
|
||||
@@ -1530,12 +1536,12 @@ function peg$parse(input, options) {
|
||||
var s0, s1, s2, s3;
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 4) === peg$c27) {
|
||||
s1 = peg$c27;
|
||||
if (input.substr(peg$currPos, 4) === peg$c28) {
|
||||
s1 = peg$c28;
|
||||
peg$currPos += 4;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e34); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e35); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsews();
|
||||
@@ -1559,12 +1565,12 @@ function peg$parse(input, options) {
|
||||
var s0, s1, s2, s3, s4, s5;
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 5) === peg$c28) {
|
||||
s1 = peg$c28;
|
||||
if (input.substr(peg$currPos, 5) === peg$c29) {
|
||||
s1 = peg$c29;
|
||||
peg$currPos += 5;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e35); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e36); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsews();
|
||||
@@ -1609,12 +1615,12 @@ function peg$parse(input, options) {
|
||||
var s0, s1, s2, s3;
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 2) === peg$c29) {
|
||||
s1 = peg$c29;
|
||||
if (input.substr(peg$currPos, 2) === peg$c30) {
|
||||
s1 = peg$c30;
|
||||
peg$currPos += 2;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e36); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e37); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = [];
|
||||
@@ -1623,7 +1629,7 @@ function peg$parse(input, options) {
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s3 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e37); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e38); }
|
||||
}
|
||||
while (s3 !== peg$FAILED) {
|
||||
s2.push(s3);
|
||||
@@ -1632,7 +1638,7 @@ function peg$parse(input, options) {
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s3 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e37); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e38); }
|
||||
}
|
||||
}
|
||||
s1 = [s1, s2];
|
||||
@@ -1649,21 +1655,21 @@ function peg$parse(input, options) {
|
||||
var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 3) === peg$c30) {
|
||||
s1 = peg$c30;
|
||||
if (input.substr(peg$currPos, 3) === peg$c31) {
|
||||
s1 = peg$c31;
|
||||
peg$currPos += 3;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e38); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e39); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsews();
|
||||
if (input.charCodeAt(peg$currPos) === 91) {
|
||||
s3 = peg$c10;
|
||||
s3 = peg$c11;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s3 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e17); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e18); }
|
||||
}
|
||||
if (s3 !== peg$FAILED) {
|
||||
s4 = peg$parsews();
|
||||
@@ -1705,11 +1711,11 @@ function peg$parse(input, options) {
|
||||
}
|
||||
s7 = peg$parsews();
|
||||
if (input.charCodeAt(peg$currPos) === 93) {
|
||||
s8 = peg$c11;
|
||||
s8 = peg$c12;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s8 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e18); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e19); }
|
||||
}
|
||||
if (s8 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
@@ -1770,11 +1776,11 @@ function peg$parse(input, options) {
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsews();
|
||||
if (input.charCodeAt(peg$currPos) === 36) {
|
||||
s3 = peg$c31;
|
||||
s3 = peg$c32;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s3 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e39); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e40); }
|
||||
}
|
||||
if (s3 !== peg$FAILED) {
|
||||
s4 = peg$parsews();
|
||||
@@ -1852,12 +1858,12 @@ function peg$parse(input, options) {
|
||||
var s0, s1, s2, s3;
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 6) === peg$c32) {
|
||||
s1 = peg$c32;
|
||||
if (input.substr(peg$currPos, 6) === peg$c33) {
|
||||
s1 = peg$c33;
|
||||
peg$currPos += 6;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e40); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e41); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsews();
|
||||
@@ -1881,12 +1887,12 @@ function peg$parse(input, options) {
|
||||
var s0, s1, s2, s3;
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 6) === peg$c33) {
|
||||
s1 = peg$c33;
|
||||
if (input.substr(peg$currPos, 6) === peg$c34) {
|
||||
s1 = peg$c34;
|
||||
peg$currPos += 6;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e41); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e42); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parsews();
|
||||
@@ -1910,12 +1916,12 @@ function peg$parse(input, options) {
|
||||
var s0, s1;
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 4) === peg$c34) {
|
||||
s1 = peg$c34;
|
||||
if (input.substr(peg$currPos, 4) === peg$c35) {
|
||||
s1 = peg$c35;
|
||||
peg$currPos += 4;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e42); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e43); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
@@ -1938,35 +1944,34 @@ function peg$parse(input, options) {
|
||||
}
|
||||
|
||||
|
||||
var PatternStub = function(source, alignment)
|
||||
{
|
||||
this.type_ = "pattern";
|
||||
this.arguments_ = { alignment : alignment};
|
||||
this.source_ = source;
|
||||
}
|
||||
var PatternStub = function(source, alignment)
|
||||
{
|
||||
this.type_ = "pattern";
|
||||
this.arguments_ = { alignment : alignment};
|
||||
this.source_ = source;
|
||||
}
|
||||
|
||||
var OperatorStub = function(name, args, source)
|
||||
{
|
||||
this.type_ = name;
|
||||
this.arguments_ = args;
|
||||
this.source_ = source;
|
||||
}
|
||||
var OperatorStub = function(name, args, source)
|
||||
{
|
||||
this.type_ = name;
|
||||
this.arguments_ = args;
|
||||
this.source_ = source;
|
||||
}
|
||||
|
||||
var ElementStub = function(source, options)
|
||||
{
|
||||
this.type_ = "element";
|
||||
this.source_ = source;
|
||||
this.options_ = options;
|
||||
this.location_ = location();
|
||||
}
|
||||
|
||||
var CommandStub = function(name, options)
|
||||
{
|
||||
this.type_ = "command";
|
||||
this.name_ = name;
|
||||
this.options_ = options;
|
||||
}
|
||||
var ElementStub = function(source, options)
|
||||
{
|
||||
this.type_ = "element";
|
||||
this.source_ = source;
|
||||
this.options_ = options;
|
||||
this.location_ = location();
|
||||
}
|
||||
|
||||
var CommandStub = function(name, options)
|
||||
{
|
||||
this.type_ = "command";
|
||||
this.name_ = name;
|
||||
this.options_ = options;
|
||||
}
|
||||
|
||||
|
||||
peg$result = peg$startRuleFunction();
|
||||
@@ -1990,5 +1995,6 @@ function peg$parse(input, options) {
|
||||
|
||||
export {
|
||||
peg$SyntaxError as SyntaxError,
|
||||
|
||||
peg$parse as parse
|
||||
};
|
||||
|
||||
@@ -87,7 +87,7 @@ quote = '"' / "'"
|
||||
// ------------------ steps and cycles ---------------------------
|
||||
|
||||
// single step definition (e.g bd)
|
||||
step_char = [0-9a-zA-Z~] / "-" / "#" / "." / "^" / "_"
|
||||
step_char = [0-9a-zA-Z~] / "-" / "#" / "." / "^" / "_" / ":"
|
||||
step = ws chars:step_char+ ws { return chars.join("") }
|
||||
|
||||
// define a sub cycle e.g. [1 2, 3 [4]]
|
||||
|
||||
Generated
+1031
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"name": "@strudel.cycles/mini",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"description": "Mini notation for strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "mocha --colors"
|
||||
"test": "mocha --colors",
|
||||
"build:parser": "peggy -o krill-parser.js --format es ./krill.pegjs"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -25,8 +26,11 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.1.1",
|
||||
"@strudel.cycles/eval": "^0.1.2",
|
||||
"@strudel.cycles/tone": "^0.1.2"
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"@strudel.cycles/eval": "^0.1.3",
|
||||
"@strudel.cycles/tone": "^0.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"peggy": "^2.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+35
-10
@@ -145,6 +145,37 @@ const materialPalenightHighlightStyle = HighlightStyle.define([
|
||||
// : Extension
|
||||
const materialPalenight = [materialPalenightTheme, materialPalenightHighlightStyle];
|
||||
|
||||
const setFlash = StateEffect.define();
|
||||
const flashField = StateField.define({
|
||||
create() {
|
||||
return Decoration.none;
|
||||
},
|
||||
update(flash2, tr) {
|
||||
try {
|
||||
for (let e of tr.effects) {
|
||||
if (e.is(setFlash)) {
|
||||
if (e.value) {
|
||||
const mark = Decoration.mark({ attributes: { style: `background-color: #FFCA2880` } });
|
||||
flash2 = Decoration.set([mark.range(0, tr.newDoc.length)]);
|
||||
} else {
|
||||
flash2 = Decoration.set([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return flash2;
|
||||
} catch (err) {
|
||||
console.warn("flash error", err);
|
||||
return flash2;
|
||||
}
|
||||
},
|
||||
provide: (f) => EditorView.decorations.from(f)
|
||||
});
|
||||
const flash = (view) => {
|
||||
view.dispatch({ effects: setFlash.of(true) });
|
||||
setTimeout(() => {
|
||||
view.dispatch({ effects: setFlash.of(false) });
|
||||
}, 200);
|
||||
};
|
||||
const setHighlights = StateEffect.define();
|
||||
const highlightField = StateField.define({
|
||||
create() {
|
||||
@@ -187,7 +218,8 @@ function CodeMirror({ value, onChange, onViewChanged, onCursor, options, editorD
|
||||
extensions: [
|
||||
javascript(),
|
||||
materialPalenight,
|
||||
highlightField
|
||||
highlightField,
|
||||
flashField
|
||||
]
|
||||
}));
|
||||
}
|
||||
@@ -340,14 +372,7 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw: onDrawP
|
||||
/* console.warn('no instrument chosen', event);
|
||||
throw new Error(`no instrument chosen for ${JSON.stringify(event)}`); */
|
||||
} else {
|
||||
onTrigger(
|
||||
time,
|
||||
event,
|
||||
currentTime,
|
||||
1 /* cps */,
|
||||
event.wholeOrPart().begin.valueOf(),
|
||||
event.duration.valueOf(),
|
||||
);
|
||||
onTrigger(time, event, currentTime, 1 /* cps */);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
@@ -623,4 +648,4 @@ function useWebMidi(props) {
|
||||
return { loading, outputs, outputByName };
|
||||
}
|
||||
|
||||
export { CodeMirror, MiniRepl, cx, useCycle, useHighlighting, usePostMessage, useRepl, useWebMidi };
|
||||
export { CodeMirror, MiniRepl, cx, flash, useCycle, useHighlighting, usePostMessage, useRepl, useWebMidi };
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/react",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/react",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.4",
|
||||
"description": "React components for strudel",
|
||||
"main": "dist/index.cjs.js",
|
||||
"module": "dist/index.es.js",
|
||||
@@ -38,9 +38,9 @@
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@codemirror/lang-javascript": "^0.19.0",
|
||||
"@strudel.cycles/core": "^0.1.1",
|
||||
"@strudel.cycles/eval": "^0.1.2",
|
||||
"@strudel.cycles/tone": "^0.1.2",
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"@strudel.cycles/eval": "^0.1.3",
|
||||
"@strudel.cycles/tone": "^0.1.3",
|
||||
"react-codemirror6": "^1.1.0",
|
||||
"react-hook-inview": "^4.5.0"
|
||||
},
|
||||
|
||||
@@ -7,6 +7,39 @@ import { javascript } from '@codemirror/lang-javascript';
|
||||
// import { materialPalenight } from 'codemirror6-themes';
|
||||
import { materialPalenight } from '../themes/material-palenight';
|
||||
|
||||
export const setFlash = StateEffect.define();
|
||||
const flashField = StateField.define({
|
||||
create() {
|
||||
return Decoration.none;
|
||||
},
|
||||
update(flash, tr) {
|
||||
try {
|
||||
for (let e of tr.effects) {
|
||||
if (e.is(setFlash)) {
|
||||
if (e.value) {
|
||||
const mark = Decoration.mark({ attributes: { style: `background-color: #FFCA2880` } });
|
||||
flash = Decoration.set([mark.range(0, tr.newDoc.length)]);
|
||||
} else {
|
||||
flash = Decoration.set([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return flash;
|
||||
} catch (err) {
|
||||
console.warn('flash error', err);
|
||||
return flash;
|
||||
}
|
||||
},
|
||||
provide: (f) => EditorView.decorations.from(f),
|
||||
});
|
||||
|
||||
export const flash = (view) => {
|
||||
view.dispatch({ effects: setFlash.of(true) });
|
||||
setTimeout(() => {
|
||||
view.dispatch({ effects: setFlash.of(false) });
|
||||
}, 200);
|
||||
};
|
||||
|
||||
export const setHighlights = StateEffect.define();
|
||||
const highlightField = StateField.define({
|
||||
create() {
|
||||
@@ -61,6 +94,7 @@ export default function CodeMirror({ value, onChange, onViewChanged, onCursor, o
|
||||
javascript(),
|
||||
materialPalenight,
|
||||
highlightField,
|
||||
flashField,
|
||||
// theme, language, ...
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// import 'tailwindcss/tailwind.css';
|
||||
|
||||
export { default as CodeMirror } from './components/CodeMirror6';
|
||||
export { default as CodeMirror, flash } from './components/CodeMirror6';
|
||||
export * from './components/MiniRepl';
|
||||
export { default as useCycle } from './hooks/useCycle';
|
||||
export { default as useHighlighting } from './hooks/useHighlighting';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/serial",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.3",
|
||||
"description": "Webserial API for strudel",
|
||||
"main": "serial.mjs",
|
||||
"repository": {
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Pattern, isPattern } from '@strudel.cycles/core';
|
||||
var serialWriter;
|
||||
var choosing = false;
|
||||
|
||||
export async function getWriter(br=115200) {
|
||||
export async function getWriter(br=38400) {
|
||||
if (choosing) {
|
||||
return;
|
||||
}
|
||||
@@ -34,8 +34,7 @@ export async function getWriter(br=115200) {
|
||||
|
||||
const latency = 0.1;
|
||||
|
||||
// Pattern.prototype.midi = function (output: string | number, channel = 1) {
|
||||
Pattern.prototype.serial = async function (...args) {
|
||||
Pattern.prototype.serial = function (...args) {
|
||||
return this._withHap((hap) => {
|
||||
if (!serialWriter) {
|
||||
getWriter(...args);
|
||||
@@ -43,8 +42,27 @@ Pattern.prototype.serial = async function (...args) {
|
||||
const onTrigger = (time, hap, currentTime) => {
|
||||
var message = "";
|
||||
if (typeof hap.value === 'object') {
|
||||
for (const [key, val] of Object.entries(hap.value).flat()) {
|
||||
message += `${key}:${val};`
|
||||
if ('action' in hap.value) {
|
||||
message += hap.value['action'] + '(';
|
||||
var first = true;
|
||||
for (const [key, val] of Object.entries(hap.value)) {
|
||||
if (key === 'action') {
|
||||
continue;
|
||||
}
|
||||
if (first) {
|
||||
first = false;
|
||||
}
|
||||
else {
|
||||
message +=',';
|
||||
}
|
||||
message += `${key}:${val}`
|
||||
}
|
||||
message += ')';
|
||||
}
|
||||
else {
|
||||
for (const [key, val] of Object.entries(hap.value)) {
|
||||
message += `${key}:${val};`
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// this script converts a soundfont into a json file, it has not been not used yet
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
const name = '0000_JCLive';
|
||||
|
||||
const js = await fetch(`https://felixroos.github.io/webaudiofontdata/sound/${name}_sf2_file.js`).then((res) =>
|
||||
res.text(),
|
||||
);
|
||||
// console.log(js);
|
||||
|
||||
let [_, data] = js.split('_sf2_file=');
|
||||
data = eval(data);
|
||||
console.log(JSON.stringify(data));
|
||||
@@ -0,0 +1,114 @@
|
||||
let loadCache = {};
|
||||
async function loadFont(name) {
|
||||
if (loadCache[name]) {
|
||||
return loadCache[name];
|
||||
}
|
||||
const load = async () => {
|
||||
// TODO: make soundfont source configurable
|
||||
const url = `https://felixroos.github.io/webaudiofontdata/sound/${name}.js`;
|
||||
console.log('load font', name, url);
|
||||
const preset = await fetch(url).then((res) => res.text());
|
||||
let [_, data] = preset.split('={');
|
||||
return eval('{' + data);
|
||||
};
|
||||
loadCache[name] = load();
|
||||
return loadCache[name];
|
||||
}
|
||||
|
||||
export async function getFontBufferSource(name, pitch, ac) {
|
||||
if (typeof pitch === 'string') {
|
||||
pitch = toMidi(pitch);
|
||||
}
|
||||
const { buffer, zone } = await getFontPitch(name, pitch, ac);
|
||||
const src = ac.createBufferSource();
|
||||
src.buffer = buffer;
|
||||
const baseDetune = zone.originalPitch - 100.0 * zone.coarseTune - zone.fineTune;
|
||||
const playbackRate = 1.0 * Math.pow(2, (100.0 * pitch - baseDetune) / 1200.0);
|
||||
// src detune?
|
||||
src.playbackRate.value = playbackRate;
|
||||
const loop = zone.loopStart > 1 && zone.loopStart < zone.loopEnd;
|
||||
if (!loop) {
|
||||
/* const waveDuration = duration + this.afterTime;
|
||||
if (waveDuration > zone.buffer.duration / playbackRate) {
|
||||
waveDuration = zone.buffer.duration / playbackRate;
|
||||
// TODO: do sth with waveduration
|
||||
} */
|
||||
} else {
|
||||
src.loop = true;
|
||||
src.loopStart = zone.loopStart / zone.sampleRate;
|
||||
src.loopEnd = zone.loopEnd / zone.sampleRate;
|
||||
//+ (zone.delay ? zone.delay : 0);
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
let bufferCache = {};
|
||||
export async function getFontPitch(name, pitch, ac) {
|
||||
const key = `${name}:::${pitch}`;
|
||||
if (bufferCache[key]) {
|
||||
return bufferCache[key];
|
||||
}
|
||||
// console.log('load buffer', key);
|
||||
const load = async () => {
|
||||
const preset = await loadFont(name);
|
||||
if (!preset) {
|
||||
throw new Error(`Could not load soundfont ${name}`);
|
||||
}
|
||||
const zone = findZone(preset, pitch);
|
||||
if (!zone) {
|
||||
throw new Error('no soundfont zone found for preset ', name, 'pitch', pitch);
|
||||
}
|
||||
const buffer = await getBuffer(zone, ac);
|
||||
if (!buffer) {
|
||||
throw new Error(`no soundfont buffer found for preset ${name}, pitch: ${pitch}`);
|
||||
}
|
||||
return { buffer, zone };
|
||||
};
|
||||
bufferCache[key] = load(); // dont await here to cache promise immediately!
|
||||
return bufferCache[key];
|
||||
}
|
||||
|
||||
function findZone(preset, pitch) {
|
||||
return preset.find((zone) => {
|
||||
return zone.keyRangeLow <= pitch && zone.keyRangeHigh + 1 >= pitch;
|
||||
});
|
||||
}
|
||||
|
||||
// promisified version of https://github.com/felixroos/webaudiofont/blob/c6f97249b60dcfafc20fca5bb381294a6b2f8f51/npm/dist/WebAudioFontPlayer.js#L740
|
||||
async function getBuffer(zone, audioContext) {
|
||||
if (zone.sample) {
|
||||
console.warn('zone.sample untested!');
|
||||
const decoded = atob(zone.sample);
|
||||
zone.buffer = audioContext.createBuffer(1, decoded.length / 2, zone.sampleRate);
|
||||
const float32Array = zone.buffer.getChannelData(0);
|
||||
let b1, b2, n;
|
||||
for (var i = 0; i < decoded.length / 2; i++) {
|
||||
b1 = decoded.charCodeAt(i * 2);
|
||||
b2 = decoded.charCodeAt(i * 2 + 1);
|
||||
if (b1 < 0) {
|
||||
b1 = 256 + b1;
|
||||
}
|
||||
if (b2 < 0) {
|
||||
b2 = 256 + b2;
|
||||
}
|
||||
n = b2 * 256 + b1;
|
||||
if (n >= 65536 / 2) {
|
||||
n = n - 65536;
|
||||
}
|
||||
float32Array[i] = n / 65536.0;
|
||||
}
|
||||
} else {
|
||||
if (zone.file) {
|
||||
const datalen = zone.file.length;
|
||||
const arraybuffer = new ArrayBuffer(datalen);
|
||||
const view = new Uint8Array(arraybuffer);
|
||||
const decoded = atob(zone.file);
|
||||
let b;
|
||||
for (let i = 0; i < decoded.length; i++) {
|
||||
b = decoded.charCodeAt(i);
|
||||
view[i] = b;
|
||||
}
|
||||
return new Promise((resolve) => audioContext.decodeAudioData(arraybuffer, resolve));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { getFontBufferSource } from './fontloader.mjs';
|
||||
import * as soundfontList from './list.mjs';
|
||||
|
||||
globalThis.getFontBufferSource = getFontBufferSource;
|
||||
globalThis.soundfontList = soundfontList;
|
||||
globalThis.soundfontList = soundfontList;
|
||||
File diff suppressed because it is too large
Load Diff
Generated
+301
@@ -0,0 +1,301 @@
|
||||
{
|
||||
"name": "@strudel.cycles/soundfonts",
|
||||
"version": "0.1.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@strudel.cycles/soundfonts",
|
||||
"version": "0.1.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"webaudiofont": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "8.0.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
|
||||
"integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==",
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^5.0.1",
|
||||
"once": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"dependencies": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/jsonc-parser": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz",
|
||||
"integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA=="
|
||||
},
|
||||
"node_modules/lunr": {
|
||||
"version": "2.3.9",
|
||||
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
|
||||
"integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow=="
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "4.0.17",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-4.0.17.tgz",
|
||||
"integrity": "sha512-Wfk0ATOK5iPxM4ptrORkFemqroz0ZDxp5MWfYA7H/F+wO17NRWV5Ypxi6p3g2Xmw2bKeiYOl6oVnLHKxBA0VhA==",
|
||||
"bin": {
|
||||
"marked": "bin/marked.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz",
|
||||
"integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/shiki": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz",
|
||||
"integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==",
|
||||
"dependencies": {
|
||||
"jsonc-parser": "^3.0.0",
|
||||
"vscode-oniguruma": "^1.6.1",
|
||||
"vscode-textmate": "5.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typedoc": {
|
||||
"version": "0.22.17",
|
||||
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.17.tgz",
|
||||
"integrity": "sha512-h6+uXHVVCPDaANzjwzdsj9aePBjZiBTpiMpBBeyh1zcN2odVsDCNajz8zyKnixF93HJeGpl34j/70yoEE5BfNg==",
|
||||
"dependencies": {
|
||||
"glob": "^8.0.3",
|
||||
"lunr": "^2.3.9",
|
||||
"marked": "^4.0.16",
|
||||
"minimatch": "^5.1.0",
|
||||
"shiki": "^0.10.1"
|
||||
},
|
||||
"bin": {
|
||||
"typedoc": "bin/typedoc"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x || 4.6.x || 4.7.x"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.7.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
|
||||
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-oniguruma": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz",
|
||||
"integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA=="
|
||||
},
|
||||
"node_modules/vscode-textmate": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz",
|
||||
"integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ=="
|
||||
},
|
||||
"node_modules/webaudiofont": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webaudiofont/-/webaudiofont-3.0.1.tgz",
|
||||
"integrity": "sha512-PwqbR4OdvzbW8/TLTitSU/rw4mxMKyAGTKV42mePCgMoUGLJoQj8HDLWVqtNOpUZ0ghPNhNE6cRFEv6HFFEBQA==",
|
||||
"dependencies": {
|
||||
"typedoc": "^0.22.14"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
||||
},
|
||||
"glob": {
|
||||
"version": "8.0.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
|
||||
"integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==",
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^5.0.1",
|
||||
"once": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"jsonc-parser": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz",
|
||||
"integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA=="
|
||||
},
|
||||
"lunr": {
|
||||
"version": "2.3.9",
|
||||
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
|
||||
"integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow=="
|
||||
},
|
||||
"marked": {
|
||||
"version": "4.0.17",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-4.0.17.tgz",
|
||||
"integrity": "sha512-Wfk0ATOK5iPxM4ptrORkFemqroz0ZDxp5MWfYA7H/F+wO17NRWV5Ypxi6p3g2Xmw2bKeiYOl6oVnLHKxBA0VhA=="
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz",
|
||||
"integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==",
|
||||
"requires": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"shiki": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz",
|
||||
"integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==",
|
||||
"requires": {
|
||||
"jsonc-parser": "^3.0.0",
|
||||
"vscode-oniguruma": "^1.6.1",
|
||||
"vscode-textmate": "5.2.0"
|
||||
}
|
||||
},
|
||||
"typedoc": {
|
||||
"version": "0.22.17",
|
||||
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.17.tgz",
|
||||
"integrity": "sha512-h6+uXHVVCPDaANzjwzdsj9aePBjZiBTpiMpBBeyh1zcN2odVsDCNajz8zyKnixF93HJeGpl34j/70yoEE5BfNg==",
|
||||
"requires": {
|
||||
"glob": "^8.0.3",
|
||||
"lunr": "^2.3.9",
|
||||
"marked": "^4.0.16",
|
||||
"minimatch": "^5.1.0",
|
||||
"shiki": "^0.10.1"
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.7.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
|
||||
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
|
||||
"peer": true
|
||||
},
|
||||
"vscode-oniguruma": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz",
|
||||
"integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA=="
|
||||
},
|
||||
"vscode-textmate": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz",
|
||||
"integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ=="
|
||||
},
|
||||
"webaudiofont": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webaudiofont/-/webaudiofont-3.0.1.tgz",
|
||||
"integrity": "sha512-PwqbR4OdvzbW8/TLTitSU/rw4mxMKyAGTKV42mePCgMoUGLJoQj8HDLWVqtNOpUZ0ghPNhNE6cRFEv6HFFEBQA==",
|
||||
"requires": {
|
||||
"typedoc": "^0.22.14"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "@strudel.cycles/soundfonts",
|
||||
"version": "0.1.1",
|
||||
"description": "Soundsfont support for strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/tidalcycles/strudel.git"
|
||||
},
|
||||
"keywords": [
|
||||
"tidalcycles",
|
||||
"strudel",
|
||||
"pattern",
|
||||
"livecoding",
|
||||
"algorave"
|
||||
],
|
||||
"author": "Felix Roos <flix91@gmail.com>",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"bugs": {
|
||||
"url": "https://github.com/tidalcycles/strudel/issues"
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "*",
|
||||
"@strudel.cycles/webaudio": "^0.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"node-fetch": "^3.2.6"
|
||||
}
|
||||
}
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/tonal",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/tonal",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"description": "Tonal functions for strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.1.1",
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"@tonaljs/tonal": "^4.6.5",
|
||||
"webmidi": "^3.0.15"
|
||||
}
|
||||
|
||||
@@ -36,8 +36,7 @@ Pattern.prototype.draw = function (callback, { from, to, onQuery }) {
|
||||
const begin = currentCycle + from;
|
||||
const end = currentCycle + to;
|
||||
setTimeout(() => {
|
||||
events = this._asNumber(true) // true = silent error
|
||||
.query(new State(new TimeSpan(begin, end)))
|
||||
events = this.query(new State(new TimeSpan(begin, end)))
|
||||
.filter(Boolean)
|
||||
.filter((event) => event.part.begin.equals(event.whole.begin));
|
||||
onQuery?.(events);
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/tone",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/tone",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"description": "Tone.js API for strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
@@ -22,7 +22,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.1.1",
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"@tonejs/piano": "^0.2.1",
|
||||
"chord-voicings": "^0.0.1",
|
||||
"tone": "^14.7.77"
|
||||
|
||||
@@ -7,6 +7,13 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
import { Pattern } from '@strudel.cycles/core';
|
||||
|
||||
const scale = (normalized, min, max) => normalized * (max - min) + min;
|
||||
const getValue = (e) => {
|
||||
let value = typeof e.value === 'object' ? e.value.note ?? e.value.n : e.value;
|
||||
if (typeof value === 'string') {
|
||||
value = toMidi(value);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
Pattern.prototype.pianoroll = function ({
|
||||
cycles = 4,
|
||||
@@ -75,9 +82,9 @@ Pattern.prototype.pianoroll = function ({
|
||||
ctx.stroke();
|
||||
const timePx = scale((event.whole.begin - (flipTime ? to : from)) / timeExtent, ...timeRange);
|
||||
let durationPx = scale(event.duration / timeExtent, 0, timeAxis);
|
||||
|
||||
const value = getValue(event);
|
||||
const valuePx = scale(
|
||||
fold ? foldValues.indexOf(event.value) / foldValues.length : (Number(event.value) - minMidi) / valueExtent,
|
||||
fold ? foldValues.indexOf(value) / foldValues.length : (Number(value) - minMidi) / valueExtent,
|
||||
...valueRange,
|
||||
);
|
||||
let margin = 0;
|
||||
@@ -105,7 +112,6 @@ Pattern.prototype.pianoroll = function ({
|
||||
from: from - overscan,
|
||||
to: to + overscan,
|
||||
onQuery: (events) => {
|
||||
const getValue = (e) => Number(e.value);
|
||||
const { min, max, values } = events.reduce(
|
||||
({ min, max, values }, e) => {
|
||||
const v = getValue(e);
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/webaudio",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/webaudio",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.4",
|
||||
"description": "Web Audio helpers for Strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
@@ -28,6 +28,6 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.1.1"
|
||||
"@strudel.cycles/core": "^0.1.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,16 +93,32 @@ export const loadGithubSamples = async (path, nameFn) => {
|
||||
*
|
||||
*/
|
||||
|
||||
export const samples = (sampleMap, baseUrl = sampleMap._base) => {
|
||||
export const samples = (sampleMap, baseUrl = sampleMap._base || '') => {
|
||||
sampleCache.current = {
|
||||
...sampleCache.current,
|
||||
...Object.fromEntries(
|
||||
Object.entries(sampleMap).map(([key, value]) => [
|
||||
key,
|
||||
(typeof value === 'string' ? [value] : value).map((v) =>
|
||||
(baseUrl + v).replace('github:', 'https://raw.githubusercontent.com/'),
|
||||
),
|
||||
]),
|
||||
Object.entries(sampleMap).map(([key, value]) => {
|
||||
if (typeof value === 'string') {
|
||||
value = [value];
|
||||
}
|
||||
if (typeof value !== 'object') {
|
||||
throw new Error('wrong sample map format for ' + key);
|
||||
}
|
||||
baseUrl = value._base || baseUrl;
|
||||
const replaceUrl = (v) => (baseUrl + v).replace('github:', 'https://raw.githubusercontent.com/');
|
||||
if (Array.isArray(value)) {
|
||||
return [key, value.map(replaceUrl)];
|
||||
}
|
||||
// must be object
|
||||
return [
|
||||
key,
|
||||
Object.fromEntries(
|
||||
Object.entries(value).map(([note, samples]) => {
|
||||
return [note, (typeof samples === 'string' ? [samples] : samples).map(replaceUrl)];
|
||||
}),
|
||||
),
|
||||
];
|
||||
}),
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
+164
-46
@@ -6,7 +6,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
|
||||
// import { Pattern, getFrequency, patternify2 } from '@strudel.cycles/core';
|
||||
import * as strudel from '@strudel.cycles/core';
|
||||
import { fromMidi } from '@strudel.cycles/core';
|
||||
import { fromMidi, toMidi } from '@strudel.cycles/core';
|
||||
import { loadBuffer } from './sampler.mjs';
|
||||
const { Pattern } = strudel;
|
||||
|
||||
@@ -39,6 +39,91 @@ const getADSR = (attack, decay, sustain, release, velocity, begin, end) => {
|
||||
return gainNode;
|
||||
};
|
||||
|
||||
const getOscillator = ({ s, freq, t, duration, release }) => {
|
||||
// make oscillator
|
||||
const o = getAudioContext().createOscillator();
|
||||
o.type = s || 'triangle';
|
||||
o.frequency.value = Number(freq);
|
||||
o.start(t);
|
||||
o.stop(t + duration + release);
|
||||
return o;
|
||||
};
|
||||
|
||||
const getSoundfontKey = (s) => {
|
||||
if (!globalThis.soundfontList) {
|
||||
// soundfont package not loaded
|
||||
return false;
|
||||
}
|
||||
if (globalThis.soundfontList?.instruments?.includes(s)) {
|
||||
return s;
|
||||
}
|
||||
// check if s is one of the soundfonts, which are loaded into globalThis, to avoid coupling both packages
|
||||
const nameIndex = globalThis.soundfontList?.instrumentNames?.indexOf(s);
|
||||
// convert number nameIndex (0-128) to 3 digit string (001-128)
|
||||
const name = nameIndex < 10 ? `00${nameIndex}` : nameIndex < 100 ? `0${nameIndex}` : nameIndex;
|
||||
if (nameIndex !== -1) {
|
||||
// TODO: indices of instrumentNames do not seem to match instruments
|
||||
return globalThis.soundfontList.instruments.find((instrument) => instrument.startsWith(name));
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
const getSampleBufferSource = async (s, n, note) => {
|
||||
let transpose = 0;
|
||||
if (note) {
|
||||
transpose = toMidi(note) - 36; // C3 is middle C
|
||||
}
|
||||
const ac = getAudioContext();
|
||||
// is sample from loaded samples(..)
|
||||
const samples = getLoadedSamples();
|
||||
if (!samples) {
|
||||
throw new Error('no samples loaded');
|
||||
}
|
||||
const bank = samples?.[s];
|
||||
if (!bank) {
|
||||
throw new Error('sample not found:', s, 'try one of ' + Object.keys(samples));
|
||||
}
|
||||
if (typeof bank !== 'object') {
|
||||
throw new Error('wrong format for sample bank:', s);
|
||||
}
|
||||
let sampleUrl;
|
||||
if (Array.isArray(bank)) {
|
||||
sampleUrl = bank[n % bank.length];
|
||||
} else {
|
||||
if (!note) {
|
||||
throw new Error('no note(...) set for sound', s);
|
||||
}
|
||||
const midiDiff = (noteA) => toMidi(noteA) - toMidi(note);
|
||||
// object format will expect keys as notes
|
||||
const closest = Object.keys(bank)
|
||||
.filter((k) => !k.startsWith('_'))
|
||||
.reduce(
|
||||
(closest, key, j) => (!closest || Math.abs(midiDiff(key)) < Math.abs(midiDiff(closest)) ? key : closest),
|
||||
null,
|
||||
);
|
||||
transpose = -midiDiff(closest); // semitones to repitch
|
||||
sampleUrl = bank[closest][n % bank[closest].length];
|
||||
}
|
||||
const buffer = await loadBuffer(sampleUrl, ac);
|
||||
const bufferSource = ac.createBufferSource();
|
||||
bufferSource.buffer = buffer;
|
||||
const playbackRate = 1.0 * Math.pow(2, transpose / 12);
|
||||
// bufferSource.playbackRate.value = Math.pow(2, transpose / 12);
|
||||
bufferSource.playbackRate.value = playbackRate;
|
||||
return bufferSource;
|
||||
};
|
||||
|
||||
const splitSN = (s, n) => {
|
||||
if (!s.includes(':')) {
|
||||
return [s, n];
|
||||
}
|
||||
let [s2, n2] = s.split(':');
|
||||
if (isNaN(Number(n2))) {
|
||||
return [s, n];
|
||||
}
|
||||
return [s2, n2];
|
||||
};
|
||||
|
||||
Pattern.prototype.out = function () {
|
||||
return this.onTrigger(async (t, hap, ct) => {
|
||||
const ac = getAudioContext();
|
||||
@@ -48,7 +133,10 @@ Pattern.prototype.out = function () {
|
||||
let {
|
||||
freq,
|
||||
s,
|
||||
sf,
|
||||
clip = 0, // if 1, samples will be cut off when the hap ends
|
||||
n = 0,
|
||||
note,
|
||||
gain = 1,
|
||||
cutoff,
|
||||
resonance = 1,
|
||||
@@ -58,33 +146,39 @@ Pattern.prototype.out = function () {
|
||||
bandq = 1,
|
||||
pan,
|
||||
attack = 0.001,
|
||||
decay = 0,
|
||||
sustain = 1,
|
||||
decay = 0.05,
|
||||
sustain = 0.5,
|
||||
release = 0.001,
|
||||
speed = 1, // sample playback speed
|
||||
begin = 0,
|
||||
end = 1,
|
||||
} = hap.value;
|
||||
const { velocity = 1 } = hap.context;
|
||||
gain *= velocity; // legacy fix for velocity
|
||||
// the chain will hold all audio nodes that connect to each other
|
||||
const chain = [];
|
||||
if (typeof s === 'string') {
|
||||
[s, n] = splitSN(s, n);
|
||||
}
|
||||
if (typeof note === 'string') {
|
||||
[note, n] = splitSN(note, n);
|
||||
}
|
||||
if (!s || ['sine', 'square', 'triangle', 'sawtooth'].includes(s)) {
|
||||
// with synths, n and note are the same thing
|
||||
n = note || n;
|
||||
if (typeof n === 'string') {
|
||||
n = toMidi(n); // e.g. c3 => 48
|
||||
}
|
||||
// get frequency
|
||||
if (!freq && typeof n === 'number') {
|
||||
freq = fromMidi(n); // + 48);
|
||||
}
|
||||
if (!freq && typeof n === 'string') {
|
||||
freq = fromMidi(toMidi(n));
|
||||
}
|
||||
// make oscillator
|
||||
const o = ac.createOscillator();
|
||||
o.type = s || 'triangle';
|
||||
o.frequency.value = Number(freq);
|
||||
o.start(t);
|
||||
o.stop(t + hap.duration + release);
|
||||
const o = getOscillator({ t, s, freq, duration: hap.duration, release });
|
||||
chain.push(o);
|
||||
// level down oscillators as they are really loud compared to samples i've tested
|
||||
const g = ac.createGain();
|
||||
g.gain.value = 0.5;
|
||||
g.gain.value = 0.3;
|
||||
chain.push(g);
|
||||
// TODO: make adsr work with samples without pops
|
||||
// envelope
|
||||
@@ -92,44 +186,68 @@ Pattern.prototype.out = function () {
|
||||
chain.push(adsr);
|
||||
} else {
|
||||
// load sample
|
||||
const samples = getLoadedSamples();
|
||||
if (!samples) {
|
||||
console.warn('no samples loaded');
|
||||
if (speed === 0) {
|
||||
// no playback
|
||||
return;
|
||||
}
|
||||
const bank = samples?.[s];
|
||||
if (!bank) {
|
||||
console.warn('sample not found:', s, 'try one of ' + Object.keys(samples));
|
||||
if (!s) {
|
||||
console.warn('no sample specified');
|
||||
return;
|
||||
} else {
|
||||
if (speed === 0) {
|
||||
// no playback
|
||||
return;
|
||||
}
|
||||
if (!s) {
|
||||
console.warn('no sample specified');
|
||||
return;
|
||||
}
|
||||
const bank = samples[s];
|
||||
const sampleUrl = bank[n % bank.length];
|
||||
let buffer = await loadBuffer(sampleUrl, ac);
|
||||
if (ac.currentTime > t) {
|
||||
console.warn('sample still loading:', s, n);
|
||||
return;
|
||||
}
|
||||
const src = ac.createBufferSource();
|
||||
src.buffer = buffer;
|
||||
src.playbackRate.value = Math.abs(speed);
|
||||
// TODO: nudge, unit, cut, loop
|
||||
}
|
||||
const soundfont = getSoundfontKey(s);
|
||||
let bufferSource;
|
||||
|
||||
let duration = src.buffer.duration;
|
||||
const offset = begin * duration;
|
||||
duration = ((end - begin) * duration) / Math.abs(speed);
|
||||
src.start(t, offset, duration);
|
||||
src.stop(t + duration);
|
||||
chain.push(src);
|
||||
try {
|
||||
if (soundfont) {
|
||||
// is soundfont
|
||||
bufferSource = await globalThis.getFontBufferSource(soundfont, note || n, ac);
|
||||
} else {
|
||||
// is sample from loaded samples(..)
|
||||
bufferSource = await getSampleBufferSource(s, n, note);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
return;
|
||||
}
|
||||
// asny stuff above took too long?
|
||||
if (ac.currentTime > t) {
|
||||
console.warn('sample still loading:', s, n);
|
||||
return;
|
||||
}
|
||||
if (!bufferSource) {
|
||||
console.warn('no buffer source');
|
||||
return;
|
||||
}
|
||||
bufferSource.playbackRate.value = Math.abs(speed) * bufferSource.playbackRate.value;
|
||||
// TODO: nudge, unit, cut, loop
|
||||
let duration = soundfont || clip ? hap.duration : bufferSource.buffer.duration;
|
||||
// let duration = bufferSource.buffer.duration;
|
||||
const offset = begin * duration;
|
||||
duration = ((end - begin) * duration) / Math.abs(speed);
|
||||
if (soundfont || clip) {
|
||||
bufferSource.start(t, offset); // duration does not work here for some reason
|
||||
} else {
|
||||
bufferSource.start(t, offset, duration);
|
||||
}
|
||||
chain.push(bufferSource);
|
||||
if (soundfont || clip) {
|
||||
const env = ac.createGain();
|
||||
const releaseLength = 0.1;
|
||||
env.gain.value = 0.6;
|
||||
env.gain.setValueAtTime(env.gain.value, t + duration);
|
||||
env.gain.linearRampToValueAtTime(0, t + duration + releaseLength);
|
||||
// env.gain.linearRampToValueAtTime(0, t + duration + releaseLength);
|
||||
chain.push(env);
|
||||
bufferSource.stop(t + duration + releaseLength);
|
||||
} else {
|
||||
bufferSource.stop(t + duration);
|
||||
}
|
||||
}
|
||||
// master out
|
||||
const master = ac.createGain();
|
||||
master.gain.value = gain;
|
||||
chain.push(master);
|
||||
|
||||
// filters
|
||||
cutoff !== undefined && chain.push(getFilter('lowpass', cutoff, resonance));
|
||||
hcutoff !== undefined && chain.push(getFilter('highpass', hcutoff, hresonance));
|
||||
@@ -143,9 +261,9 @@ Pattern.prototype.out = function () {
|
||||
chain.push(panner);
|
||||
}
|
||||
// master out
|
||||
const master = ac.createGain();
|
||||
/* const master = ac.createGain();
|
||||
master.gain.value = 0.8 * gain;
|
||||
chain.push(master);
|
||||
chain.push(master); */
|
||||
chain.push(ac.destination);
|
||||
// connect chain elements together
|
||||
chain.slice(1).reduce((last, current) => last.connect(current), chain[0]);
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/webdirt",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/webdirt",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"description": "WebDirt integration for Strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
@@ -22,7 +22,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.1.1",
|
||||
"@strudel.cycles/core": "^0.1.2",
|
||||
"WebDirt": "github:dktr0/WebDirt"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/xen",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"description": "Xenharmonic API for strudel",
|
||||
"main": "index.mjs",
|
||||
"scripts": {
|
||||
@@ -24,6 +24,6 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "^0.1.1"
|
||||
"@strudel.cycles/core": "^0.1.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,636 +0,0 @@
|
||||
{
|
||||
"_info": "Auto-generated by pandoc-url2cite. Feel free to modify, keys will never be overwritten.",
|
||||
"urls": {
|
||||
"https://www.tandfonline.com/doi/abs/10.1080/14794713.2016.1227602?journalCode=rpdm20": {
|
||||
"fetched": "2022-03-24T12:17:27.481Z",
|
||||
"bibtex": [
|
||||
"",
|
||||
"@article{roberts_code_2016,",
|
||||
" title = {Code as information and code as spectacle},",
|
||||
" volume = {12},",
|
||||
" issn = {1479-4713},",
|
||||
" url = {https://doi.org/10.1080/14794713.2016.1227602},",
|
||||
" doi = {10.1080/14794713.2016.1227602},",
|
||||
" abstract = {In this artist statement, I will discuss the tension between source code as an interactive system for performers and source code as information and entertainment for audiences in live-coding performances. I then describe augmentations I developed for the presentation of source code in the live-coding environment Gibber, including animations and annotations that visually reveal aspects of system state during performances. I briefly describe audience responses to these techniques and, more importantly, how they are critical to my own artistic practice.},",
|
||||
" number = {2},",
|
||||
" urldate = {2022-03-24},",
|
||||
" journal = {International Journal of Performance Arts and Digital Media},",
|
||||
" author = {Roberts, Charles},",
|
||||
" month = jul,",
|
||||
" year = {2016},",
|
||||
" keywords = {Live coding, psychology of programming, notation, audiences, algorithms},",
|
||||
" pages = {201--206},",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"csl": {
|
||||
"DOI": "10.1080/14794713.2016.1227602",
|
||||
"ISSN": "1479-4713",
|
||||
"URL": "https://doi.org/10.1080/14794713.2016.1227602",
|
||||
"abstract": "In this artist statement, I will discuss the tension between source code as an interactive system for performers and source code as information and entertainment for audiences in live-coding performances. I then describe augmentations I developed for the presentation of source code in the live-coding environment Gibber, including animations and annotations that visually reveal aspects of system state during performances. I briefly describe audience responses to these techniques and, more importantly, how they are critical to my own artistic practice.",
|
||||
"accessed": {
|
||||
"date-parts": [
|
||||
[
|
||||
2022,
|
||||
3,
|
||||
24
|
||||
]
|
||||
]
|
||||
},
|
||||
"author": [
|
||||
{
|
||||
"family": "Roberts",
|
||||
"given": "Charles"
|
||||
}
|
||||
],
|
||||
"container-title": "International Journal of Performance Arts and Digital Media",
|
||||
"id": "https://www.tandfonline.com/doi/abs/10.1080/14794713.2016.1227602?journalCode_x61_rpdm20",
|
||||
"issue": "2",
|
||||
"issued": {
|
||||
"date-parts": [
|
||||
[
|
||||
2016,
|
||||
7
|
||||
]
|
||||
]
|
||||
},
|
||||
"keyword": "Live coding, psychology of programming, notation, audiences, algorithms",
|
||||
"page": "201-206",
|
||||
"title": "Code as information and code as spectacle",
|
||||
"type": "article-journal",
|
||||
"volume": "12"
|
||||
}
|
||||
},
|
||||
"https://zenodo.org/record/5788732": {
|
||||
"fetched": "2022-03-24T12:20:17.218Z",
|
||||
"bibtex": [
|
||||
"",
|
||||
"@misc{mclean_alternate_2021,",
|
||||
" address = {Valdivia, Chile},",
|
||||
" title = {Alternate {Timelines} for {TidalCycles}},",
|
||||
" url = {https://zenodo.org/record/5788732},",
|
||||
" abstract = {The TidalCycles (or Tidal for short) live coding environment has been developed since around 2009, via several rewrites of its core representation. Rather than having fixed goals, this development has been guided by use, motivated by the open aim to make music. This development process can be seen as a long-form improvisation, with insights into the nature of Tidal gained through the process of writing it, feeding back to guide the next steps of development. This brings the worrying thought that key insights will have been missed along this development journey, that would otherwise have lead to very different software. Indeed participants at beginners’ workshops that I have lead or co-lead have often asked questions without good answers, because they made deficiencies or missing features in the software clear. It is well known that a beginner’s mind is able to see much that an expert has become blind to. Running workshops are an excellent way to find new development ideas, but the present paper explores a different technique – the rewrite.},",
|
||||
" urldate = {2022-03-24},",
|
||||
" collaborator = {McLean, Alex},",
|
||||
" month = dec,",
|
||||
" year = {2021},",
|
||||
" keywords = {live coding, algorithmic pattern, tidalcycles, haskell, python},",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"csl": {
|
||||
"URL": "https://zenodo.org/record/5788732",
|
||||
"abstract": "The TidalCycles (or Tidal for short) live coding environment has been developed since around 2009, via several rewrites of its core representation. Rather than having fixed goals, this development has been guided by use, motivated by the open aim to make music. This development process can be seen as a long-form improvisation, with insights into the nature of Tidal gained through the process of writing it, feeding back to guide the next steps of development. This brings the worrying thought that key insights will have been missed along this development journey, that would otherwise have lead to very different software. Indeed participants at beginners’ workshops that I have lead or co-lead have often asked questions without good answers, because they made deficiencies or missing features in the software clear. It is well known that a beginner’s mind is able to see much that an expert has become blind to. Running workshops are an excellent way to find new development ideas, but the present paper explores a different technique – the rewrite.",
|
||||
"accessed": {
|
||||
"date-parts": [
|
||||
[
|
||||
2022,
|
||||
3,
|
||||
24
|
||||
]
|
||||
]
|
||||
},
|
||||
"id": "https://zenodo.org/record/5788732",
|
||||
"issued": {
|
||||
"date-parts": [
|
||||
[
|
||||
2021,
|
||||
12
|
||||
]
|
||||
]
|
||||
},
|
||||
"keyword": "live coding, algorithmic pattern, tidalcycles, haskell, python",
|
||||
"publisher-place": "Valdivia, Chile",
|
||||
"title": "Alternate Timelines for TidalCycles",
|
||||
"type": ""
|
||||
}
|
||||
},
|
||||
"https://www.semanticscholar.org/paper/Bringing-the-TidalCycles-Mini-Notation-to-the-Roberts/74965efadd572ae3f40d14c633a5c8581c1b9f42": {
|
||||
"fetched": "2022-04-12T22:47:17.063Z",
|
||||
"bibtex": [
|
||||
"",
|
||||
"@misc{roberts_bringing_2019,",
|
||||
" title = {Bringing the {TidalCycles} {Mini}-{Notation} to the {Browser}},",
|
||||
" url = {https://www.semanticscholar.org/paper/Bringing-the-TidalCycles-Mini-Notation-to-the-Roberts/74965efadd572ae3f40d14c633a5c8581c1b9f42},",
|
||||
" abstract = {A JavaScript dialect of its mini-notation for pattern is created, enabling easy integration with creative coding tools and an accompanying technique for visually annotating the playback of TidalCycles patterns over time. TidalCycles has rapidly become the most popular system for many styles of live coding performance, in particular Algoraves. We created a JavaScript dialect of its mini-notation for pattern, enabling easy integration with creative coding tools. Our research pairs a formalism describing the mini-notation with a small JavaScript library for generating events over time; this library is suitable for generating events inside of an AudioWorkletProcessor thread and for assisting with scheduling in JavaScript environments more generally. We describe integrating the library into the two live coding systems, Gibber and Hydra, and discuss an accompanying technique for visually annotating the playback of TidalCycles patterns over time.},",
|
||||
" language = {en},",
|
||||
" urldate = {2022-04-12},",
|
||||
" journal = {www.semanticscholar.org},",
|
||||
" author = {Roberts, Charles},",
|
||||
" year = {2019},",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"csl": {
|
||||
"URL": "https://www.semanticscholar.org/paper/Bringing-the-TidalCycles-Mini-Notation-to-the-Roberts/74965efadd572ae3f40d14c633a5c8581c1b9f42",
|
||||
"abstract": "A JavaScript dialect of its mini-notation for pattern is created, enabling easy integration with creative coding tools and an accompanying technique for visually annotating the playback of TidalCycles patterns over time. TidalCycles has rapidly become the most popular system for many styles of live coding performance, in particular Algoraves. We created a JavaScript dialect of its mini-notation for pattern, enabling easy integration with creative coding tools. Our research pairs a formalism describing the mini-notation with a small JavaScript library for generating events over time; this library is suitable for generating events inside of an AudioWorkletProcessor thread and for assisting with scheduling in JavaScript environments more generally. We describe integrating the library into the two live coding systems, Gibber and Hydra, and discuss an accompanying technique for visually annotating the playback of TidalCycles patterns over time.",
|
||||
"accessed": {
|
||||
"date-parts": [
|
||||
[
|
||||
2022,
|
||||
4,
|
||||
12
|
||||
]
|
||||
]
|
||||
},
|
||||
"author": [
|
||||
{
|
||||
"family": "Roberts",
|
||||
"given": "Charles"
|
||||
}
|
||||
],
|
||||
"container-title": "www.semanticscholar.org",
|
||||
"id": "https://www.semanticscholar.org/paper/Bringing-the-TidalCycles-Mini-Notation-to-the-Roberts/74965efadd572ae3f40d14c633a5c8581c1b9f42",
|
||||
"issued": {
|
||||
"date-parts": [
|
||||
[
|
||||
2019
|
||||
]
|
||||
]
|
||||
},
|
||||
"title": "Bringing the TidalCycles Mini-Notation to the Browser",
|
||||
"type": ""
|
||||
}
|
||||
},
|
||||
"https://zenodo.org/record/6456380": {
|
||||
"fetched": "2022-04-14T21:26:21.302Z",
|
||||
"bibtex": [
|
||||
"",
|
||||
"@misc{mclean_tidalvortex_2022,",
|
||||
" address = {Limerick, Ireland},",
|
||||
" title = {{TidalVortex} {Zero}},",
|
||||
" url = {https://zenodo.org/record/6456380},",
|
||||
" abstract = {In this paper we introduce ‘version zero’ of TidalVortex, an alternative implementation of the TidalCycles live coding system, using the Python programming language. This is open-ended work, exploring what happens when we try to extract the 'essence' of a system like TidalCycles and translate it into another programming language, while taking advantage of the affordance of its new host. First, we review the substantial prior art in porting TidalCycles, and in representing musical patterns in Python. We then compare equivalent patterns written in Haskell (TidalCycles) and Python (TidalVortex), and relate implementation details of how functional reactive paradigms have translated from the pure functional, strongly typed Haskell to the more multi-paradigm, dynamically typed Python. Finally, we conclude with reflections and generalisable outcomes.},",
|
||||
" urldate = {2022-04-14},",
|
||||
" collaborator = {McLean, Alex and Forment, Raphaël and Le Beux, Sylvain and Silvani, Damián},",
|
||||
" month = apr,",
|
||||
" year = {2022},",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"csl": {
|
||||
"URL": "https://zenodo.org/record/6456380",
|
||||
"abstract": "In this paper we introduce “version zero” of TidalVortex, an alternative implementation of the TidalCycles live coding system, using the Python programming language. This is open-ended work, exploring what happens when we try to extract the ’essence’ of a system like TidalCycles and translate it into another programming language, while taking advantage of the affordance of its new host. First, we review the substantial prior art in porting TidalCycles, and in representing musical patterns in Python. We then compare equivalent patterns written in Haskell (TidalCycles) and Python (TidalVortex), and relate implementation details of how functional reactive paradigms have translated from the pure functional, strongly typed Haskell to the more multi-paradigm, dynamically typed Python. Finally, we conclude with reflections and generalisable outcomes.",
|
||||
"accessed": {
|
||||
"date-parts": [
|
||||
[
|
||||
2022,
|
||||
4,
|
||||
14
|
||||
]
|
||||
]
|
||||
},
|
||||
"id": "https://zenodo.org/record/6456380",
|
||||
"issued": {
|
||||
"date-parts": [
|
||||
[
|
||||
2022,
|
||||
4
|
||||
]
|
||||
]
|
||||
},
|
||||
"publisher-place": "Limerick, Ireland",
|
||||
"title": "TidalVortex Zero",
|
||||
"type": ""
|
||||
}
|
||||
},
|
||||
"https://zenodo.org/record/4299661": {
|
||||
"fetched": "2022-04-15T07:40:08.702Z",
|
||||
"bibtex": [
|
||||
"",
|
||||
"@misc{mclean_algorithmic_2020,",
|
||||
" address = {Birmingham UK},",
|
||||
" title = {Algorithmic {Pattern}},",
|
||||
" url = {https://zenodo.org/record/4299661},",
|
||||
" abstract = {This paper brings together two main perspectives on algorithmic pattern. First, the writing of musical patterns in live coding performance, and second, the weaving of patterns in textiles. In both cases, algorithmic pattern is an interface between the human and the outcome, where small changes have far-reaching impact on the results. By bringing contemporary live coding and ancient textile approaches together, we reach a common view of pattern as algorithmic movement (e.g. looping, shifting, reflecting, interfering) in the making of things. This works beyond the usual definition of pattern used in musical interfaces, of mere repeating sequences. We conclude by considering the place of algorithmic pattern in a wider activity of making.},",
|
||||
" urldate = {2022-04-15},",
|
||||
" collaborator = {McLean, Alex},",
|
||||
" month = jul,",
|
||||
" year = {2020},",
|
||||
" keywords = {pattern, tidalcycles, algorithmic music, textiles, live coding, algorave},",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"csl": {
|
||||
"URL": "https://zenodo.org/record/4299661",
|
||||
"abstract": "This paper brings together two main perspectives on algorithmic pattern. First, the writing of musical patterns in live coding performance, and second, the weaving of patterns in textiles. In both cases, algorithmic pattern is an interface between the human and the outcome, where small changes have far-reaching impact on the results. By bringing contemporary live coding and ancient textile approaches together, we reach a common view of pattern as algorithmic movement (e.g. looping, shifting, reflecting, interfering) in the making of things. This works beyond the usual definition of pattern used in musical interfaces, of mere repeating sequences. We conclude by considering the place of algorithmic pattern in a wider activity of making.",
|
||||
"accessed": {
|
||||
"date-parts": [
|
||||
[
|
||||
2022,
|
||||
4,
|
||||
15
|
||||
]
|
||||
]
|
||||
},
|
||||
"id": "https://zenodo.org/record/4299661",
|
||||
"issued": {
|
||||
"date-parts": [
|
||||
[
|
||||
2020,
|
||||
7
|
||||
]
|
||||
]
|
||||
},
|
||||
"keyword": "pattern, tidalcycles, algorithmic music, textiles, live coding, algorave",
|
||||
"publisher-place": "Birmingham UK",
|
||||
"title": "Algorithmic Pattern",
|
||||
"type": ""
|
||||
}
|
||||
},
|
||||
"https://quod.lib.umich.edu/i/icmc/bbp2372.2012.011/2/–gibber-live-coding-audio-in-the-browser?page=root;size=150;view=text": {
|
||||
"fetched": "2022-04-15T07:40:15.037Z",
|
||||
"bibtex": [
|
||||
"",
|
||||
"@article{charlie_gibber:_2012,",
|
||||
" title = {{GIBBER}: {LIVE} {CODING} {AUDIO} {IN} {THE} {BROWSER}},",
|
||||
" volume = {2012},",
|
||||
" issn = {2223-3881},",
|
||||
" url = {https://quod.lib.umich.edu/i/icmc/bbp2372.2012.011/2/%E2%80%93gibber-live-coding-audio-in-the-browser?page=root;size=150;view=text},",
|
||||
" language = {en},",
|
||||
" urldate = {2022-04-15},",
|
||||
" journal = {International Computer Music Conference Proceedings},",
|
||||
" author = {Charlie, , Roberts and Joann, , Kuchera-Morin},",
|
||||
" year = {2012},",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"csl": {
|
||||
"ISSN": "2223-3881",
|
||||
"URL": "https://quod.lib.umich.edu/i/icmc/bbp2372.2012.011/2/%E2%80%93gibber-live-coding-audio-in-the-browser?page=root;size=150;view=text",
|
||||
"accessed": {
|
||||
"date-parts": [
|
||||
[
|
||||
2022,
|
||||
4,
|
||||
15
|
||||
]
|
||||
]
|
||||
},
|
||||
"author": [
|
||||
{
|
||||
"family": "Charlie",
|
||||
"given": "Roberts"
|
||||
},
|
||||
{
|
||||
"family": "Joann",
|
||||
"given": "Kuchera-Morin"
|
||||
}
|
||||
],
|
||||
"container-title": "International Computer Music Conference Proceedings",
|
||||
"id": "https://quod.lib.umich.edu/i/icmc/bbp2372.2012.011/2/–gibber-live-coding-audio-in-the-browser?page_x61_root;size_x61_150;view_x61_text",
|
||||
"issued": {
|
||||
"date-parts": [
|
||||
[
|
||||
2012
|
||||
]
|
||||
]
|
||||
},
|
||||
"title": "GIBBER: LIVE CODING AUDIO IN THE BROWSER",
|
||||
"title-short": "GIBBER",
|
||||
"type": "article-journal",
|
||||
"volume": "2012"
|
||||
}
|
||||
},
|
||||
"https://www.semanticscholar.org/paper/Estuary%3A-Browser-based-Collaborative-Projectional-Ogborn-Beverley/c6b5d34575d6230dfd8751ca4af8e5f6e44d916b": {
|
||||
"fetched": "2022-04-15T07:40:17.179Z",
|
||||
"bibtex": [
|
||||
"",
|
||||
"@misc{ogborn_estuary:_2017,",
|
||||
" title = {Estuary: {Browser}-based {Collaborative} {Projectional} {Live} {Coding} of {Musical} {Patterns}},",
|
||||
" shorttitle = {Estuary},",
|
||||
" url = {https://www.semanticscholar.org/paper/Estuary%3A-Browser-based-Collaborative-Projectional-Ogborn-Beverley/c6b5d34575d6230dfd8751ca4af8e5f6e44d916b},",
|
||||
" abstract = {Estuary is a browser-based collaborative projectional editing environment built on top of the popular TidalCycles language for the live coding of musical pattern that includes a strict form of structure editing, a click-only border-free approach to interface design, and explicit notations to modulate the liveness of different parts of the code. This paper describes the initial design and development of Estuary, a browser-based collaborative projectional editing environment built on top of the popular TidalCycles language for the live coding of musical pattern. Key features of Estuary include a strict form of structure editing (making syntactical errors impossible), a click-only border-free approach to interface design, explicit notations to modulate the liveness of different parts of the code, and a server-based network collaboration system that can be used for many simultaneous collaborative live coding performances, as well as to present different views of the same live coding activity. Estuary has been developed using Reflex-DOM, a Haskell-based framework for web development whose strictness promises robustness and security advantages.},",
|
||||
" language = {en},",
|
||||
" urldate = {2022-04-15},",
|
||||
" journal = {www.semanticscholar.org},",
|
||||
" author = {Ogborn, David and Beverley, J.},",
|
||||
" year = {2017},",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"csl": {
|
||||
"URL": "https://www.semanticscholar.org/paper/Estuary%3A-Browser-based-Collaborative-Projectional-Ogborn-Beverley/c6b5d34575d6230dfd8751ca4af8e5f6e44d916b",
|
||||
"abstract": "Estuary is a browser-based collaborative projectional editing environment built on top of the popular TidalCycles language for the live coding of musical pattern that includes a strict form of structure editing, a click-only border-free approach to interface design, and explicit notations to modulate the liveness of different parts of the code. This paper describes the initial design and development of Estuary, a browser-based collaborative projectional editing environment built on top of the popular TidalCycles language for the live coding of musical pattern. Key features of Estuary include a strict form of structure editing (making syntactical errors impossible), a click-only border-free approach to interface design, explicit notations to modulate the liveness of different parts of the code, and a server-based network collaboration system that can be used for many simultaneous collaborative live coding performances, as well as to present different views of the same live coding activity. Estuary has been developed using Reflex-DOM, a Haskell-based framework for web development whose strictness promises robustness and security advantages.",
|
||||
"accessed": {
|
||||
"date-parts": [
|
||||
[
|
||||
2022,
|
||||
4,
|
||||
15
|
||||
]
|
||||
]
|
||||
},
|
||||
"author": [
|
||||
{
|
||||
"family": "Ogborn",
|
||||
"given": "David"
|
||||
},
|
||||
{
|
||||
"family": "Beverley",
|
||||
"given": "J."
|
||||
}
|
||||
],
|
||||
"container-title": "www.semanticscholar.org",
|
||||
"id": "https://www.semanticscholar.org/paper/Estuary_x37_3A-Browser-based-Collaborative-Projectional-Ogborn-Beverley/c6b5d34575d6230dfd8751ca4af8e5f6e44d916b",
|
||||
"issued": {
|
||||
"date-parts": [
|
||||
[
|
||||
2017
|
||||
]
|
||||
]
|
||||
},
|
||||
"title": "Estuary: Browser-based Collaborative Projectional Live Coding of Musical Patterns",
|
||||
"title-short": "Estuary",
|
||||
"type": ""
|
||||
}
|
||||
},
|
||||
"https://zenodo.org/record/6353969": {
|
||||
"fetched": "2022-04-15T07:40:20.966Z",
|
||||
"bibtex": [
|
||||
"",
|
||||
"@misc{mclean_feedforward_2020,",
|
||||
" address = {Birmingham},",
|
||||
" title = {Feedforward},",
|
||||
" url = {https://zenodo.org/record/6353969},",
|
||||
" abstract = {This is an improvised, from-scratch live coding performance. The NIME interface which this performance showcases is the new Feedfoward editor for the TidalCycles live coding environment. Feedforward is written in Haskell using the ncurses library for terminal-based user interfaces. It runs on low-powered hardware including the Raspberry Pi Zero, with formative testing of prototypes conducted with several groups of children between the ages of 8 and 14. Feedforward has a number of features designed to support improvised, multi-pattern live coding. Individual Tidal patterns are addressable with hotkeys for fast mute and unmuting. Each pattern has a stereo VU meter, to aid the quick matching of sound to pattern within a mix. In addition, TidalCycles has been extended to store context with each event, so that source code positions in its polyrhythmic sequence mini-notation are tracked. This allows steps to be highlighted in the source code when- ever they are active. This works even when Tidal combinators have been applied to manipulate the timeline. Formal evaluation has yet to take place, but this feature appears to support learning of how pattern manipulations work in Tidal. Feedforward and TidalCycles is free/open source software under a GPL licence version 3.0.},",
|
||||
" urldate = {2022-04-15},",
|
||||
" collaborator = {McLean, Alex},",
|
||||
" month = jul,",
|
||||
" year = {2020},",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"csl": {
|
||||
"URL": "https://zenodo.org/record/6353969",
|
||||
"abstract": "This is an improvised, from-scratch live coding performance. The NIME interface which this performance showcases is the new Feedfoward editor for the TidalCycles live coding environment. Feedforward is written in Haskell using the ncurses library for terminal-based user interfaces. It runs on low-powered hardware including the Raspberry Pi Zero, with formative testing of prototypes conducted with several groups of children between the ages of 8 and 14. Feedforward has a number of features designed to support improvised, multi-pattern live coding. Individual Tidal patterns are addressable with hotkeys for fast mute and unmuting. Each pattern has a stereo VU meter, to aid the quick matching of sound to pattern within a mix. In addition, TidalCycles has been extended to store context with each event, so that source code positions in its polyrhythmic sequence mini-notation are tracked. This allows steps to be highlighted in the source code when- ever they are active. This works even when Tidal combinators have been applied to manipulate the timeline. Formal evaluation has yet to take place, but this feature appears to support learning of how pattern manipulations work in Tidal. Feedforward and TidalCycles is free/open source software under a GPL licence version 3.0.",
|
||||
"accessed": {
|
||||
"date-parts": [
|
||||
[
|
||||
2022,
|
||||
4,
|
||||
15
|
||||
]
|
||||
]
|
||||
},
|
||||
"id": "https://zenodo.org/record/6353969",
|
||||
"issued": {
|
||||
"date-parts": [
|
||||
[
|
||||
2020,
|
||||
7
|
||||
]
|
||||
]
|
||||
},
|
||||
"publisher-place": "Birmingham",
|
||||
"title": "Feedforward",
|
||||
"type": ""
|
||||
}
|
||||
},
|
||||
"https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.72.1340": {
|
||||
"fetched": "2022-04-24T21:09:16.724Z",
|
||||
"bibtex": [
|
||||
"",
|
||||
"@inproceedings{toussaint_euclidean_2005,",
|
||||
" title = {The {Euclidean} algorithm generates traditional musical rhythms},",
|
||||
" url = {https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.72.1340},",
|
||||
" abstract = {The Euclidean algorithm (which comes down to us from Euclid’s Elements) computes the greatest common divisor of two given integers. It is shown here that the structure of the Euclidean algorithm may be used to automatically generate, very efficiently, a large family of rhythms used as timelines (rhythmic ostinatos), in traditional world music. These rhythms, here dubbed Euclidean rhythms, have the property that their onset patterns are distributed as evenly as possible in a mathematically precise sense, and optimal manner. Euclidean rhythms are closely related to the family of Aksak rhythms studied by ethnomusicologists, and occur in a wide variety of other disciplines as well. For example they characterize algorithms for drawing digital straight lines in computer graphics, as well as algorithms for calculating leap years in calendar design. Euclidean rhythms also find application in nuclear physics accelerators and in computer science, and are closely related to several families of words and sequences of interest in the study of the combinatorics of words, such as mechanical words, Sturmian words, two-distance sequences, and Euclidean strings, to which the Euclidean rhythms are compared. 1.},",
|
||||
" urldate = {2022-04-24},",
|
||||
" booktitle = {In {Proceedings} of {BRIDGES}: {Mathematical} {Connections} in {Art}, {Music} and {Science}},",
|
||||
" author = {Toussaint, Godfried},",
|
||||
" year = {2005},",
|
||||
" pages = {47--56},",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"csl": {
|
||||
"URL": "https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.72.1340",
|
||||
"abstract": "The Euclidean algorithm (which comes down to us from Euclid’s Elements) computes the greatest common divisor of two given integers. It is shown here that the structure of the Euclidean algorithm may be used to automatically generate, very efficiently, a large family of rhythms used as timelines (rhythmic ostinatos), in traditional world music. These rhythms, here dubbed Euclidean rhythms, have the property that their onset patterns are distributed as evenly as possible in a mathematically precise sense, and optimal manner. Euclidean rhythms are closely related to the family of Aksak rhythms studied by ethnomusicologists, and occur in a wide variety of other disciplines as well. For example they characterize algorithms for drawing digital straight lines in computer graphics, as well as algorithms for calculating leap years in calendar design. Euclidean rhythms also find application in nuclear physics accelerators and in computer science, and are closely related to several families of words and sequences of interest in the study of the combinatorics of words, such as mechanical words, Sturmian words, two-distance sequences, and Euclidean strings, to which the Euclidean rhythms are compared. 1.",
|
||||
"accessed": {
|
||||
"date-parts": [
|
||||
[
|
||||
2022,
|
||||
4,
|
||||
24
|
||||
]
|
||||
]
|
||||
},
|
||||
"author": [
|
||||
{
|
||||
"family": "Toussaint",
|
||||
"given": "Godfried"
|
||||
}
|
||||
],
|
||||
"container-title": "In Proceedings of BRIDGES: Mathematical Connections in Art, Music and Science",
|
||||
"id": "https://citeseerx.ist.psu.edu/viewdoc/summary?doi_x61_10.1.1.72.1340",
|
||||
"issued": {
|
||||
"date-parts": [
|
||||
[
|
||||
2005
|
||||
]
|
||||
]
|
||||
},
|
||||
"page": "47-56",
|
||||
"title": "The Euclidean algorithm generates traditional musical rhythms",
|
||||
"type": "paper-conference"
|
||||
}
|
||||
},
|
||||
"https://webaudioconf.com/posts/2021_8/": {
|
||||
"fetched": "2022-04-24T21:14:10.409Z",
|
||||
"bibtex": [
|
||||
"",
|
||||
"@misc{noauthor_wac_nodate,",
|
||||
" title = {{WAC} {\\textbar} {Glicol}: {A} {Graph}-oriented {Live} {Coding} {Language} {Developed} with {Rust}, {WebAssembly} and {AudioWorklet}},",
|
||||
" url = {https://webaudioconf.com/posts/2021_8/},",
|
||||
" urldate = {2022-04-24},",
|
||||
" journal = {webaudioconf.com},",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"csl": {
|
||||
"URL": "https://webaudioconf.com/posts/2021_8/",
|
||||
"accessed": {
|
||||
"date-parts": [
|
||||
[
|
||||
2022,
|
||||
4,
|
||||
24
|
||||
]
|
||||
]
|
||||
},
|
||||
"container-title": "webaudioconf.com",
|
||||
"id": "https://webaudioconf.com/posts/2021_8/",
|
||||
"title": "WAC Glicol: A Graph-oriented Live Coding Language Developed with Rust, WebAssembly and AudioWorklet",
|
||||
"title-short": "WAC Glicol",
|
||||
"type": ""
|
||||
}
|
||||
},
|
||||
"https://webaudioconf.com/posts/2019_38/": {
|
||||
"fetched": "2022-04-24T21:14:46.954Z",
|
||||
"bibtex": [
|
||||
"",
|
||||
"@misc{noauthor_wac_nodate,",
|
||||
" title = {{WAC} {\\textbar} {FAUST} online {IDE}: dynamically compile and publish {FAUST} code as {WebAudio} {Plugins}},",
|
||||
" url = {https://webaudioconf.com/posts/2019_38/},",
|
||||
" urldate = {2022-04-24},",
|
||||
" journal = {webaudioconf.com},",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"csl": {
|
||||
"URL": "https://webaudioconf.com/posts/2019_38/",
|
||||
"accessed": {
|
||||
"date-parts": [
|
||||
[
|
||||
2022,
|
||||
4,
|
||||
24
|
||||
]
|
||||
]
|
||||
},
|
||||
"container-title": "webaudioconf.com",
|
||||
"id": "https://webaudioconf.com/posts/2019_38/",
|
||||
"title": "WAC FAUST online IDE: Dynamically compile and publish FAUST code as WebAudio Plugins",
|
||||
"title-short": "WAC FAUST online IDE",
|
||||
"type": ""
|
||||
}
|
||||
},
|
||||
"https://strudel.tidalcycles.org": {
|
||||
"fetched": "2022-04-24T21:14:47.822Z",
|
||||
"bibtex": [
|
||||
"",
|
||||
"@misc{noauthor_strudel_nodate,",
|
||||
" title = {Strudel {REPL}},",
|
||||
" url = {https://strudel.tidalcycles.org/},",
|
||||
" abstract = {Strudel REPL},",
|
||||
" urldate = {2022-04-24},",
|
||||
" journal = {strudel.tidalcycles.org},",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"csl": {
|
||||
"URL": "https://strudel.tidalcycles.org/",
|
||||
"abstract": "Strudel REPL",
|
||||
"accessed": {
|
||||
"date-parts": [
|
||||
[
|
||||
2022,
|
||||
4,
|
||||
24
|
||||
]
|
||||
]
|
||||
},
|
||||
"container-title": "strudel.tidalcycles.org",
|
||||
"id": "https://strudel.tidalcycles.org",
|
||||
"title": "Strudel REPL",
|
||||
"type": ""
|
||||
}
|
||||
},
|
||||
"https://hydra.ojack.xyz/docs/#/": {
|
||||
"fetched": "2022-04-25T09:03:25.132Z",
|
||||
"bibtex": [
|
||||
"",
|
||||
"@misc{noauthor_hydra_nodate,",
|
||||
" title = {Hydra},",
|
||||
" url = {https://hydra.ojack.xyz/docs/#/},",
|
||||
" abstract = {Description},",
|
||||
" urldate = {2022-04-25},",
|
||||
" journal = {hydra.ojack.xyz},",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"csl": {
|
||||
"URL": "https://hydra.ojack.xyz/docs/#/",
|
||||
"abstract": "Description",
|
||||
"accessed": {
|
||||
"date-parts": [
|
||||
[
|
||||
2022,
|
||||
4,
|
||||
25
|
||||
]
|
||||
]
|
||||
},
|
||||
"container-title": "hydra.ojack.xyz",
|
||||
"id": "https://hydra.ojack.xyz/docs/_x35_/",
|
||||
"title": "Hydra",
|
||||
"type": ""
|
||||
}
|
||||
},
|
||||
"https://mikesol.github.io/purescript-wags/": {
|
||||
"fetched": "2022-04-25T09:03:26.456Z",
|
||||
"bibtex": [
|
||||
"",
|
||||
"@misc{noauthor_wags_nodate,",
|
||||
" title = {Wags documentation},",
|
||||
" url = {https://mikesol.github.io/purescript-wags/},",
|
||||
" urldate = {2022-04-25},",
|
||||
" journal = {mikesol.github.io},",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"csl": {
|
||||
"URL": "https://mikesol.github.io/purescript-wags/",
|
||||
"accessed": {
|
||||
"date-parts": [
|
||||
[
|
||||
2022,
|
||||
4,
|
||||
25
|
||||
]
|
||||
]
|
||||
},
|
||||
"container-title": "mikesol.github.io",
|
||||
"id": "https://mikesol.github.io/purescript-wags/",
|
||||
"title": "Wags documentation",
|
||||
"type": ""
|
||||
}
|
||||
},
|
||||
"https://github.com/tidalcycles/strudel": {
|
||||
"fetched": "2022-04-25T09:15:32.518Z",
|
||||
"bibtex": [
|
||||
"",
|
||||
"@misc{noauthor_strudel_2022,",
|
||||
" title = {strudel},",
|
||||
" copyright = {GPL-3.0},",
|
||||
" url = {https://github.com/tidalcycles/strudel},",
|
||||
" abstract = {Experimental port of tidalcycles to Javascript},",
|
||||
" urldate = {2022-04-25},",
|
||||
" publisher = {TidalCycles},",
|
||||
" month = apr,",
|
||||
" year = {2022},",
|
||||
" note = {original-date: 2022-01-22T20:24:35Z},",
|
||||
" keywords = {javascript, livecoding, tidal, tidalcycles, algorave, algorithmic-patterns},",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"csl": {
|
||||
"URL": "https://github.com/tidalcycles/strudel",
|
||||
"abstract": "Experimental port of tidalcycles to Javascript",
|
||||
"accessed": {
|
||||
"date-parts": [
|
||||
[
|
||||
2022,
|
||||
4,
|
||||
25
|
||||
]
|
||||
]
|
||||
},
|
||||
"id": "https://github.com/tidalcycles/strudel",
|
||||
"issued": {
|
||||
"date-parts": [
|
||||
[
|
||||
2022,
|
||||
4
|
||||
]
|
||||
]
|
||||
},
|
||||
"keyword": "javascript, livecoding, tidal, tidalcycles, algorave, algorithmic-patterns",
|
||||
"note": "original-date: 2022-01-22T20:24:35Z",
|
||||
"publisher": "TidalCycles",
|
||||
"title": "Strudel",
|
||||
"type": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
+68
-406
@@ -1,354 +1,7 @@
|
||||
---
|
||||
date: 2022-04-15
|
||||
references:
|
||||
- abstract: In this artist statement, I will discuss the tension between
|
||||
source code as an interactive system for performers and source code
|
||||
as information and entertainment for audiences in live-coding
|
||||
performances. I then describe augmentations I developed for the
|
||||
presentation of source code in the live-coding environment Gibber,
|
||||
including animations and annotations that visually reveal aspects of
|
||||
system state during performances. I briefly describe audience
|
||||
responses to these techniques and, more importantly, how they are
|
||||
critical to my own artistic practice.
|
||||
accessed:
|
||||
date-parts:
|
||||
- - 2022
|
||||
- 3
|
||||
- 24
|
||||
author:
|
||||
- family: Roberts
|
||||
given: Charles
|
||||
container-title: International Journal of Performance Arts and Digital
|
||||
Media
|
||||
DOI: 10.1080/14794713.2016.1227602
|
||||
id: "https://www.tandfonline.com/doi/abs/10.1080/14794713.2016.1227602?journalCode_x61_rpdm20"
|
||||
ISSN: 1479-4713
|
||||
issue: 2
|
||||
issued:
|
||||
date-parts:
|
||||
- - 2016
|
||||
- 7
|
||||
keyword: Live coding, psychology of programming, notation, audiences,
|
||||
algorithms
|
||||
page: 201-206
|
||||
title: Code as information and code as spectacle
|
||||
type: article-journal
|
||||
URL: "https://doi.org/10.1080/14794713.2016.1227602"
|
||||
volume: 12
|
||||
- abstract: The TidalCycles (or Tidal for short) live coding environment
|
||||
has been developed since around 2009, via several rewrites of its
|
||||
core representation. Rather than having fixed goals, this
|
||||
development has been guided by use, motivated by the open aim to
|
||||
make music. This development process can be seen as a long-form
|
||||
improvisation, with insights into the nature of Tidal gained through
|
||||
the process of writing it, feeding back to guide the next steps of
|
||||
development. This brings the worrying thought that key insights will
|
||||
have been missed along this development journey, that would
|
||||
otherwise have lead to very different software. Indeed participants
|
||||
at beginners' workshops that I have lead or co-lead have often asked
|
||||
questions without good answers, because they made deficiencies or
|
||||
missing features in the software clear. It is well known that a
|
||||
beginner's mind is able to see much that an expert has become blind
|
||||
to. Running workshops are an excellent way to find new development
|
||||
ideas, but the present paper explores a different technique -- the
|
||||
rewrite.
|
||||
accessed:
|
||||
date-parts:
|
||||
- - 2022
|
||||
- 3
|
||||
- 24
|
||||
id: "https://zenodo.org/record/5788732"
|
||||
issued:
|
||||
date-parts:
|
||||
- - 2021
|
||||
- 12
|
||||
keyword: live coding, algorithmic pattern, tidalcycles, haskell,
|
||||
python
|
||||
publisher-place: Valdivia, Chile
|
||||
title: Alternate Timelines for TidalCycles
|
||||
URL: "https://zenodo.org/record/5788732"
|
||||
- abstract: A JavaScript dialect of its mini-notation for pattern is
|
||||
created, enabling easy integration with creative coding tools and an
|
||||
accompanying technique for visually annotating the playback of
|
||||
TidalCycles patterns over time. TidalCycles has rapidly become the
|
||||
most popular system for many styles of live coding performance, in
|
||||
particular Algoraves. We created a JavaScript dialect of its
|
||||
mini-notation for pattern, enabling easy integration with creative
|
||||
coding tools. Our research pairs a formalism describing the
|
||||
mini-notation with a small JavaScript library for generating events
|
||||
over time; this library is suitable for generating events inside of
|
||||
an AudioWorkletProcessor thread and for assisting with scheduling in
|
||||
JavaScript environments more generally. We describe integrating the
|
||||
library into the two live coding systems, Gibber and Hydra, and
|
||||
discuss an accompanying technique for visually annotating the
|
||||
playback of TidalCycles patterns over time.
|
||||
accessed:
|
||||
date-parts:
|
||||
- - 2022
|
||||
- 4
|
||||
- 12
|
||||
author:
|
||||
- family: Roberts
|
||||
given: Charles
|
||||
container-title: www.semanticscholar.org
|
||||
id: "https://www.semanticscholar.org/paper/Bringing-the-TidalCycles-Mini-Notation-to-the-Roberts/74965efadd572ae3f40d14c633a5c8581c1b9f42"
|
||||
issued:
|
||||
date-parts:
|
||||
- - 2019
|
||||
title: Bringing the TidalCycles Mini-Notation to the Browser
|
||||
URL: "https://www.semanticscholar.org/paper/Bringing-the-TidalCycles-Mini-Notation-to-the-Roberts/74965efadd572ae3f40d14c633a5c8581c1b9f42"
|
||||
- abstract: In this paper we introduce "version zero" of TidalVortex, an
|
||||
alternative implementation of the TidalCycles live coding system,
|
||||
using the Python programming language. This is open-ended work,
|
||||
exploring what happens when we try to extract the 'essence' of a
|
||||
system like TidalCycles and translate it into another programming
|
||||
language, while taking advantage of the affordance of its new host.
|
||||
First, we review the substantial prior art in porting TidalCycles,
|
||||
and in representing musical patterns in Python. We then compare
|
||||
equivalent patterns written in Haskell (TidalCycles) and Python
|
||||
(TidalVortex), and relate implementation details of how functional
|
||||
reactive paradigms have translated from the pure functional,
|
||||
strongly typed Haskell to the more multi-paradigm, dynamically typed
|
||||
Python. Finally, we conclude with reflections and generalisable
|
||||
outcomes.
|
||||
accessed:
|
||||
date-parts:
|
||||
- - 2022
|
||||
- 4
|
||||
- 14
|
||||
id: "https://zenodo.org/record/6456380"
|
||||
issued:
|
||||
date-parts:
|
||||
- - 2022
|
||||
- 4
|
||||
publisher-place: Limerick, Ireland
|
||||
title: TidalVortex Zero
|
||||
URL: "https://zenodo.org/record/6456380"
|
||||
- abstract: This paper brings together two main perspectives on
|
||||
algorithmic pattern. First, the writing of musical patterns in live
|
||||
coding performance, and second, the weaving of patterns in textiles.
|
||||
In both cases, algorithmic pattern is an interface between the human
|
||||
and the outcome, where small changes have far-reaching impact on the
|
||||
results. By bringing contemporary live coding and ancient textile
|
||||
approaches together, we reach a common view of pattern as
|
||||
algorithmic movement (e.g. looping, shifting, reflecting,
|
||||
interfering) in the making of things. This works beyond the usual
|
||||
definition of pattern used in musical interfaces, of mere repeating
|
||||
sequences. We conclude by considering the place of algorithmic
|
||||
pattern in a wider activity of making.
|
||||
accessed:
|
||||
date-parts:
|
||||
- - 2022
|
||||
- 4
|
||||
- 15
|
||||
id: "https://zenodo.org/record/4299661"
|
||||
issued:
|
||||
date-parts:
|
||||
- - 2020
|
||||
- 7
|
||||
keyword: pattern, tidalcycles, algorithmic music, textiles, live
|
||||
coding, algorave
|
||||
publisher-place: Birmingham UK
|
||||
title: Algorithmic Pattern
|
||||
URL: "https://zenodo.org/record/4299661"
|
||||
- accessed:
|
||||
date-parts:
|
||||
- - 2022
|
||||
- 4
|
||||
- 15
|
||||
author:
|
||||
- family: Charlie
|
||||
given: Roberts
|
||||
- family: Joann
|
||||
given: Kuchera-Morin
|
||||
container-title: International Computer Music Conference Proceedings
|
||||
id: "https://quod.lib.umich.edu/i/icmc/bbp2372.2012.011/2/--gibber-live-coding-audio-in-the-browser?page_x61_root;size_x61_150;view_x61_text"
|
||||
ISSN: 2223-3881
|
||||
issued:
|
||||
date-parts:
|
||||
- - 2012
|
||||
title: "GIBBER: LIVE CODING AUDIO IN THE BROWSER"
|
||||
title-short: GIBBER
|
||||
type: article-journal
|
||||
URL: "https://quod.lib.umich.edu/i/icmc/bbp2372.2012.011/2/%E2%80%93gibber-live-coding-audio-in-the-browser?page=root;size=150;view=text"
|
||||
volume: 2012
|
||||
- abstract: Estuary is a browser-based collaborative projectional
|
||||
editing environment built on top of the popular TidalCycles language
|
||||
for the live coding of musical pattern that includes a strict form
|
||||
of structure editing, a click-only border-free approach to interface
|
||||
design, and explicit notations to modulate the liveness of different
|
||||
parts of the code. This paper describes the initial design and
|
||||
development of Estuary, a browser-based collaborative projectional
|
||||
editing environment built on top of the popular TidalCycles language
|
||||
for the live coding of musical pattern. Key features of Estuary
|
||||
include a strict form of structure editing (making syntactical
|
||||
errors impossible), a click-only border-free approach to interface
|
||||
design, explicit notations to modulate the liveness of different
|
||||
parts of the code, and a server-based network collaboration system
|
||||
that can be used for many simultaneous collaborative live coding
|
||||
performances, as well as to present different views of the same live
|
||||
coding activity. Estuary has been developed using Reflex-DOM, a
|
||||
Haskell-based framework for web development whose strictness
|
||||
promises robustness and security advantages.
|
||||
accessed:
|
||||
date-parts:
|
||||
- - 2022
|
||||
- 4
|
||||
- 15
|
||||
author:
|
||||
- family: Ogborn
|
||||
given: David
|
||||
- family: Beverley
|
||||
given: J.
|
||||
container-title: www.semanticscholar.org
|
||||
id: "https://www.semanticscholar.org/paper/Estuary_x37_3A-Browser-based-Collaborative-Projectional-Ogborn-Beverley/c6b5d34575d6230dfd8751ca4af8e5f6e44d916b"
|
||||
issued:
|
||||
date-parts:
|
||||
- - 2017
|
||||
title: "Estuary: Browser-based Collaborative Projectional Live Coding
|
||||
of Musical Patterns"
|
||||
title-short: Estuary
|
||||
URL: "https://www.semanticscholar.org/paper/Estuary%3A-Browser-based-Collaborative-Projectional-Ogborn-Beverley/c6b5d34575d6230dfd8751ca4af8e5f6e44d916b"
|
||||
- abstract: This is an improvised, from-scratch live coding performance.
|
||||
The NIME interface which this performance showcases is the new
|
||||
Feedfoward editor for the TidalCycles live coding environment.
|
||||
Feedforward is written in Haskell using the ncurses library for
|
||||
terminal-based user interfaces. It runs on low-powered hardware
|
||||
including the Raspberry Pi Zero, with formative testing of
|
||||
prototypes conducted with several groups of children between the
|
||||
ages of 8 and 14. Feedforward has a number of features designed to
|
||||
support improvised, multi-pattern live coding. Individual Tidal
|
||||
patterns are addressable with hotkeys for fast mute and unmuting.
|
||||
Each pattern has a stereo VU meter, to aid the quick matching of
|
||||
sound to pattern within a mix. In addition, TidalCycles has been
|
||||
extended to store context with each event, so that source code
|
||||
positions in its polyrhythmic sequence mini-notation are tracked.
|
||||
This allows steps to be highlighted in the source code when- ever
|
||||
they are active. This works even when Tidal combinators have been
|
||||
applied to manipulate the timeline. Formal evaluation has yet to
|
||||
take place, but this feature appears to support learning of how
|
||||
pattern manipulations work in Tidal. Feedforward and TidalCycles is
|
||||
free/open source software under a GPL licence version 3.0.
|
||||
accessed:
|
||||
date-parts:
|
||||
- - 2022
|
||||
- 4
|
||||
- 15
|
||||
id: "https://zenodo.org/record/6353969"
|
||||
issued:
|
||||
date-parts:
|
||||
- - 2020
|
||||
- 7
|
||||
publisher-place: Birmingham
|
||||
title: Feedforward
|
||||
URL: "https://zenodo.org/record/6353969"
|
||||
- abstract: The Euclidean algorithm (which comes down to us from
|
||||
Euclid's Elements) computes the greatest common divisor of two given
|
||||
integers. It is shown here that the structure of the Euclidean
|
||||
algorithm may be used to automatically generate, very efficiently, a
|
||||
large family of rhythms used as timelines (rhythmic ostinatos), in
|
||||
traditional world music. These rhythms, here dubbed Euclidean
|
||||
rhythms, have the property that their onset patterns are distributed
|
||||
as evenly as possible in a mathematically precise sense, and optimal
|
||||
manner. Euclidean rhythms are closely related to the family of Aksak
|
||||
rhythms studied by ethnomusicologists, and occur in a wide variety
|
||||
of other disciplines as well. For example they characterize
|
||||
algorithms for drawing digital straight lines in computer graphics,
|
||||
as well as algorithms for calculating leap years in calendar design.
|
||||
Euclidean rhythms also find application in nuclear physics
|
||||
accelerators and in computer science, and are closely related to
|
||||
several families of words and sequences of interest in the study of
|
||||
the combinatorics of words, such as mechanical words, Sturmian
|
||||
words, two-distance sequences, and Euclidean strings, to which the
|
||||
Euclidean rhythms are compared. 1.
|
||||
accessed:
|
||||
date-parts:
|
||||
- - 2022
|
||||
- 4
|
||||
- 24
|
||||
author:
|
||||
- family: Toussaint
|
||||
given: Godfried
|
||||
container-title: "In Proceedings of BRIDGES: Mathematical Connections
|
||||
in Art, Music and Science"
|
||||
id: "https://citeseerx.ist.psu.edu/viewdoc/summary?doi_x61_10.1.1.72.1340"
|
||||
issued:
|
||||
date-parts:
|
||||
- - 2005
|
||||
page: 47-56
|
||||
title: The Euclidean algorithm generates traditional musical rhythms
|
||||
type: paper-conference
|
||||
URL: "https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.72.1340"
|
||||
- accessed:
|
||||
date-parts:
|
||||
- - 2022
|
||||
- 4
|
||||
- 24
|
||||
container-title: webaudioconf.com
|
||||
id: "https://webaudioconf.com/posts/2021_8/"
|
||||
title: "WAC Glicol: A Graph-oriented Live Coding Language Developed
|
||||
with Rust, WebAssembly and AudioWorklet"
|
||||
title-short: WAC Glicol
|
||||
URL: "https://webaudioconf.com/posts/2021_8/"
|
||||
- accessed:
|
||||
date-parts:
|
||||
- - 2022
|
||||
- 4
|
||||
- 24
|
||||
container-title: webaudioconf.com
|
||||
id: "https://webaudioconf.com/posts/2019_38/"
|
||||
title: "WAC FAUST online IDE: Dynamically compile and publish FAUST
|
||||
code as WebAudio Plugins"
|
||||
title-short: WAC FAUST online IDE
|
||||
URL: "https://webaudioconf.com/posts/2019_38/"
|
||||
- abstract: Strudel REPL
|
||||
accessed:
|
||||
date-parts:
|
||||
- - 2022
|
||||
- 4
|
||||
- 24
|
||||
container-title: strudel.tidalcycles.org
|
||||
id: "https://strudel.tidalcycles.org"
|
||||
title: Strudel REPL
|
||||
URL: "https://strudel.tidalcycles.org/"
|
||||
- abstract: Description
|
||||
accessed:
|
||||
date-parts:
|
||||
- - 2022
|
||||
- 4
|
||||
- 25
|
||||
container-title: hydra.ojack.xyz
|
||||
id: "https://hydra.ojack.xyz/docs/\\_x35\\_/"
|
||||
title: Hydra
|
||||
URL: "https://hydra.ojack.xyz/docs/#/"
|
||||
- accessed:
|
||||
date-parts:
|
||||
- - 2022
|
||||
- 4
|
||||
- 25
|
||||
container-title: mikesol.github.io
|
||||
id: "https://mikesol.github.io/purescript-wags/"
|
||||
title: Wags documentation
|
||||
URL: "https://mikesol.github.io/purescript-wags/"
|
||||
- abstract: Experimental port of tidalcycles to Javascript
|
||||
accessed:
|
||||
date-parts:
|
||||
- - 2022
|
||||
- 4
|
||||
- 25
|
||||
id: "https://github.com/tidalcycles/strudel"
|
||||
issued:
|
||||
date-parts:
|
||||
- - 2022
|
||||
- 4
|
||||
keyword: javascript, livecoding, tidal, tidalcycles, algorave,
|
||||
algorithmic-patterns
|
||||
note: "original-date: 2022-01-22T20:24:35Z"
|
||||
publisher: TidalCycles
|
||||
title: Strudel
|
||||
URL: "https://github.com/tidalcycles/strudel"
|
||||
bibliography: citations.json
|
||||
date: 2022-06-24
|
||||
title: "Strudel: Algorithmic Patterns for the Web"
|
||||
url2cite: all-links
|
||||
---
|
||||
|
||||
# Introduction
|
||||
@@ -358,9 +11,9 @@ alternative implementation of the Tidal (or 'TidalCycles') live coding
|
||||
system, using the JavaScript programming language. Strudel is an attempt
|
||||
to make live coding more accessible, by creating a system that runs
|
||||
entirely in the browser, while opening Tidal's approach to algorithmic
|
||||
patterns [@https://zenodo.org/record/4299661] up to modern audio/visual
|
||||
web technologies. The Strudel REPL is a live code editor dedicated to
|
||||
manipulating strudel patterns while they play, with builtin visual
|
||||
patterns [@mcleanAlgorithmicPattern2020a] up to modern audio/visual web
|
||||
technologies. The Strudel REPL is a live code editor dedicated to
|
||||
manipulating Strudel patterns while they play, with builtin visual
|
||||
feedback. While Strudel is written in JavaScript, the API is optimized
|
||||
for simplicity and readability by applying code transformations on the
|
||||
syntax tree level, allowing language operations that would otherwise be
|
||||
@@ -369,13 +22,11 @@ including Tone.js, Web Audio nodes, OSC (Open Sound Control) messages,
|
||||
Web Serial and Web MIDI. The project is split into multiple packages,
|
||||
allowing granular reuse in other applications. Apart from TidalCycles,
|
||||
Strudel draws inspiration from many prior existing projects like
|
||||
TidalVortex [@https://zenodo.org/record/6456380], Gibber
|
||||
[@{https://quod.lib.umich.edu/i/icmc/bbp2372.2012.011/2/–gibber-live-coding-audio-in-the-browser?page_x61_root;size_x61_150;view_x61_text}],
|
||||
Estuary
|
||||
[@https://www.semanticscholar.org/paper/Estuary_x37_3A-Browser-based-Collaborative-Projectional-Ogborn-Beverley/c6b5d34575d6230dfd8751ca4af8e5f6e44d916b],
|
||||
Hydra [@{https://hydra.ojack.xyz/docs/_x35_/}], Wags
|
||||
[@{https://mikesol.github.io/purescript-wags/}] and Feedforward
|
||||
[@https://zenodo.org/record/6353969].
|
||||
TidalVortex [@mcleanTidalVortexZero2022], Gibber
|
||||
[@robertsGibberLiveCoding2012], Estuary
|
||||
[@ogbornEstuaryBrowserbasedCollaborative2017], Hydra [@jackHydra2022],
|
||||
Ocarina [@solomonPurescriptocarina2022] and Feedforward
|
||||
[@mcleanFeedforward2020].
|
||||
|
||||
# Porting from Haskell
|
||||
|
||||
@@ -387,12 +38,12 @@ with a dynamic type system. Because Tidal leans heavily on many of
|
||||
Haskell's more unique features, it was not always clear that it could
|
||||
meaningfully be ported to a multi-paradigm scripting language. However,
|
||||
this already proved to be the case with an earlier port to Python
|
||||
\[TidalVortex; @https://zenodo.org/record/6456380\], and we have now
|
||||
\[TidalVortex; @mcleanTidalVortexZero2022\], and we have now
|
||||
successfully implemented Tidal's pure functional representation of
|
||||
patterns in Strudel, including partial application, and functor,
|
||||
applicative and monad structures. Over the past few months since the
|
||||
project started in January 2022, a large part of Tidal's functionality
|
||||
has already been ported, including it's mini-notation for polymetric
|
||||
has already been ported, including its mini-notation for polymetric
|
||||
sequences, and a large part of its library of pattern manipulations. The
|
||||
result is a terse and highly composable system, where just about
|
||||
everything is a pattern, that may be transformed and combined with other
|
||||
@@ -478,20 +129,53 @@ This line could also be expressed without mini notation:
|
||||
|
||||
Here is a short description of all the functions used:
|
||||
|
||||
- slowcat: play elements sequentially, where each lasts one cycle
|
||||
- brackets: elements inside brackets are divided equally over the time
|
||||
of their parent
|
||||
- euclid(p, s, o): place p pulses evenly over s steps, with offset o
|
||||
[@https://citeseerx.ist.psu.edu/viewdoc/summary?doi_x61_10.1.1.72.1340]
|
||||
- fast(n): speed up by n. `g3.fast(2)` will play g3 two times.
|
||||
- off(n, f): copy each event, offset it by n cycles and apply function
|
||||
f
|
||||
- legato(n): multiply duration of event with n
|
||||
- echo(t, n, v): copy each event t times, with n cycles in between
|
||||
- `slowcat`: play elements sequentially, where each lasts one cycle
|
||||
- `brackets`: elements inside brackets are divided equally over the
|
||||
time of their parent
|
||||
- `euclid(p, s, o)`: place p pulses evenly over s steps, with offset o
|
||||
[@toussaintEuclideanAlgorithmGenerates2005]
|
||||
- `fast(n)`: speed up by n. `g3.fast(2)` will play g3 two times.
|
||||
- `off(n, f)`: copy each event, offset it by n cycles and apply
|
||||
function f
|
||||
- `legato(n)`: multiply duration of event with n
|
||||
- `echo(t, n, v)`: copy each event t times, with n cycles in between
|
||||
each copy, decreasing velocity by v
|
||||
- tone(instrument): play back each event with the given Tone.js
|
||||
- `tone(instrument)`: play back each event with the given Tone.js
|
||||
instrument
|
||||
- pianoroll(): visualize events as midi notes in a pianoroll
|
||||
- `pianoroll()`: visualize events as midi notes in a pianoroll
|
||||
|
||||
# Ways to make Sound
|
||||
|
||||
To generate sound, Strudel supports different outputs:
|
||||
|
||||
- Tone.js
|
||||
- Web Audio API
|
||||
- WebDirt, a js recreation of Tidal's *Dirt* sample engine
|
||||
- OSC via osc-js
|
||||
- MIDI via WebMIDI
|
||||
|
||||
Tone.js proved to be limited for the use case of Strudel, where each
|
||||
individual event could potentially have a completely different audio
|
||||
graph. While the Web Audio API takes a *fire-and-forget* approach,
|
||||
creating a lot of Tone.js instruments and effects causes performance
|
||||
issues quickly. For that reason, we chose to search for alternatives.
|
||||
|
||||
Strudel's Web Audio API output creates a new audio graph for each event.
|
||||
It currently supports basic oscillators, sample playback, envelopes,
|
||||
filters and an experimental support for soundfonts.
|
||||
|
||||
WebDirt [@ogbornDktr0WebDirt2022] was created as part of the Estuary
|
||||
Live Coding System [@ogbornEstuaryBrowserbasedCollaborative2017], and
|
||||
proved to be a solid choice for handling samples in Strudel as well.
|
||||
|
||||
Using OSC, it is possible to send messages to SuperDirt
|
||||
[@SuperDirt2022], which is what Tidal does to generate sound. The
|
||||
downside of using OSC is that it requires the user to install
|
||||
SuperCollider and its sc3plugins library, which can be difficult.
|
||||
|
||||
The MIDI output can be used to send MIDI messages to either external
|
||||
instruments or to other programs on the same device. Web MIDI is
|
||||
currently only supported on Chromium-based browsers.
|
||||
|
||||
# Future Outlook
|
||||
|
||||
@@ -502,45 +186,23 @@ general guiding principles, Strudel aims to be
|
||||
2. consistent with Tidal's approach to pattern
|
||||
3. modular and extensible
|
||||
|
||||
The main accessibility advantage over Tidal is the zero install browser
|
||||
environment. It is not yet accessible to screen reader users, but will
|
||||
be soon with the integration of the CodeMirror 6 editor. While Strudel
|
||||
can control Tidal's SuperDirt audio system via OSC, it requires the user
|
||||
to install SuperCollider and its sc3plugins library, which can be
|
||||
difficult. Without SuperDirt, Strudel is able to output sound itself via
|
||||
Tone.js, however this is limited both in terms of available features and
|
||||
runtime performance. For the future, it is planned to integrate
|
||||
alternative sound engines such as glicol
|
||||
[@{https://webaudioconf.com/posts/2021_8/}] and faust
|
||||
[@{https://webaudioconf.com/posts/2019_38/}]. To improve compatibility
|
||||
with Tidal, more Tidal functions are planned to be ported, as well as
|
||||
full compatibility with SuperDirt. Besides sound, other ways to render
|
||||
events are being explored, such as graphical, and choreographic output.
|
||||
We are also looking into alternative ways of editing patterns, including
|
||||
For the future, it is planned to integrate alternative sound engines
|
||||
such as Glicol [@lanChaosprintGlicol2022] and Faust
|
||||
[@FaustProgrammingLanguage2022]. To improve compatibility with Tidal,
|
||||
more Tidal functions are planned to be ported, as well as full
|
||||
compatibility with SuperDirt. Besides sound, other ways to render events
|
||||
are being explored, such as graphical, and choreographic output. We are
|
||||
also looking into alternative ways of editing patterns, including
|
||||
multi-user editing for network music, parsing a novel syntax to escape
|
||||
the constraints of javascript, and developing hardware/e-textile
|
||||
interfaces.
|
||||
|
||||
# Links
|
||||
|
||||
The Strudel REPL is available at [https://strudel.tidalcycles.org
|
||||
[@https://strudel.tidalcycles.org]](https://strudel.tidalcycles.org){.uri
|
||||
cite-meta="{\"URL\":\"https://strudel.tidalcycles.org/\",\"abstract\":\"Strudel REPL\",\"accessed\":{\"date-parts\":[[2022,4,24]]},\"container-title\":\"strudel.tidalcycles.org\",\"id\":\"https://strudel.tidalcycles.org\",\"title\":\"Strudel REPL\",\"type\":\"\"}"},
|
||||
The Strudel REPL is available at <https://strudel.tidalcycles.org>,
|
||||
including an interactive tutorial. The repository is at
|
||||
[https://github.com/tidalcycles/strudel
|
||||
[@https://github.com/tidalcycles/strudel]](https://github.com/tidalcycles/strudel){.uri
|
||||
cite-meta="{\"URL\":\"https://github.com/tidalcycles/strudel\",\"abstract\":\"Experimental port of tidalcycles to Javascript\",\"accessed\":{\"date-parts\":[[2022,4,25]]},\"id\":\"https://github.com/tidalcycles/strudel\",\"issued\":{\"date-parts\":[[2022,4]]},\"keyword\":\"javascript, livecoding, tidal, tidalcycles, algorave, algorithmic-patterns\",\"note\":\"original-date: 2022-01-22T20:24:35Z\",\"publisher\":\"TidalCycles\",\"title\":\"Strudel\",\"type\":\"\"}"},
|
||||
all the code is open source under the GPL-3.0 License.
|
||||
|
||||
# Technical requirements
|
||||
|
||||
- Space for one laptop + small audio interface (20 cm x 20cm), with
|
||||
mains power.
|
||||
- Stereo sound system, either placed behind presenter (for direct
|
||||
monitoring) or with additional stereo monitors.
|
||||
- Audio from audio interface: stereo pair 6,3mm jack outputs
|
||||
(balanced)
|
||||
- Projector / screen (HDMI.)
|
||||
<https://github.com/tidalcycles/strudel>, all the code is open source
|
||||
under the GPL-3.0 License.
|
||||
|
||||
# Acknowledgments
|
||||
|
||||
@@ -549,4 +211,4 @@ free/open source software communities for inspiration and support. Alex
|
||||
McLean's work on this project is supported by a UKRI Future Leaders
|
||||
Fellowship \[grant number MR/V025260/1\].
|
||||
|
||||
# References
|
||||
# References {#references .unnumbered}
|
||||
|
||||
+38
-41
@@ -1,16 +1,16 @@
|
||||
---
|
||||
title: 'Strudel: Algorithmic Patterns for the Web'
|
||||
date: '2022-04-15'
|
||||
url2cite: all-links
|
||||
date: '2022-06-24'
|
||||
bibliography: citations.json
|
||||
---
|
||||
|
||||
# Introduction
|
||||
|
||||
This paper introduces Strudel (or sometimes 'StrudelCycles'), an alternative implementation of the Tidal (or 'TidalCycles') live coding system, using the JavaScript programming language. Strudel is an attempt to make live coding more accessible, by creating a system that runs entirely in the browser, while opening Tidal's approach to algorithmic patterns [@algorithmicpattern] up to modern audio/visual web technologies. The Strudel REPL is a live code editor dedicated to manipulating strudel patterns while they play, with builtin visual feedback. While Strudel is written in JavaScript, the API is optimized for simplicity and readability by applying code transformations on the syntax tree level, allowing language operations that would otherwise be impossible. The application supports multiple ways to output sound, including Tone.js, Web Audio nodes, OSC (Open Sound Control) messages, Web Serial and Web MIDI. The project is split into multiple packages, allowing granular reuse in other applications. Apart from TidalCycles, Strudel draws inspiration from many prior existing projects like TidalVortex [@tidalvortex], Gibber [@gibber], Estuary [@estuary], Hydra [@hydra], Wags [@wags] and Feedforward [@feedforward].
|
||||
This paper introduces Strudel (or sometimes 'StrudelCycles'), an alternative implementation of the Tidal (or 'TidalCycles') live coding system, using the JavaScript programming language. Strudel is an attempt to make live coding more accessible, by creating a system that runs entirely in the browser, while opening Tidal's approach to algorithmic patterns [@mcleanAlgorithmicPattern2020a] up to modern audio/visual web technologies. The Strudel REPL is a live code editor dedicated to manipulating Strudel patterns while they play, with builtin visual feedback. While Strudel is written in JavaScript, the API is optimized for simplicity and readability by applying code transformations on the syntax tree level, allowing language operations that would otherwise be impossible. The application supports multiple ways to output sound, including Tone.js, Web Audio nodes, OSC (Open Sound Control) messages, Web Serial and Web MIDI. The project is split into multiple packages, allowing granular reuse in other applications. Apart from TidalCycles, Strudel draws inspiration from many prior existing projects like TidalVortex [@mcleanTidalVortexZero2022], Gibber [@robertsGibberLiveCoding2012], Estuary [@ogbornEstuaryBrowserbasedCollaborative2017], Hydra [@jackHydra2022], Ocarina [@solomonPurescriptocarina2022] and Feedforward [@mcleanFeedforward2020].
|
||||
|
||||
# Porting from Haskell
|
||||
|
||||
The original Tidal is implemented as a domain specific language (DSL), embedded in the Haskell pure functional programming language, taking advantage of Haskell's terse syntax and advanced, 'strong' type system. Javascript on the other hand, is a multi-paradigm programming language, with a dynamic type system. Because Tidal leans heavily on many of Haskell's more unique features, it was not always clear that it could meaningfully be ported to a multi-paradigm scripting language. However, this already proved to be the case with an earlier port to Python [TidalVortex; @tidalvortex], and we have now successfully implemented Tidal's pure functional representation of patterns in Strudel, including partial application, and functor, applicative and monad structures. Over the past few months since the project started in January 2022, a large part of Tidal's functionality has already been ported, including it's mini-notation for polymetric sequences, and a large part of its library of pattern manipulations. The result is a terse and highly composable system, where just about everything is a pattern, that may be transformed and combined with other patterns in a myriad of ways.
|
||||
The original Tidal is implemented as a domain specific language (DSL), embedded in the Haskell pure functional programming language, taking advantage of Haskell's terse syntax and advanced, 'strong' type system. Javascript on the other hand, is a multi-paradigm programming language, with a dynamic type system. Because Tidal leans heavily on many of Haskell's more unique features, it was not always clear that it could meaningfully be ported to a multi-paradigm scripting language. However, this already proved to be the case with an earlier port to Python [TidalVortex; @mcleanTidalVortexZero2022], and we have now successfully implemented Tidal's pure functional representation of patterns in Strudel, including partial application, and functor, applicative and monad structures. Over the past few months since the project started in January 2022, a large part of Tidal's functionality has already been ported, including its mini-notation for polymetric sequences, and a large part of its library of pattern manipulations. The result is a terse and highly composable system, where just about everything is a pattern, that may be transformed and combined with other patterns in a myriad of ways.
|
||||
|
||||
# Representing Patterns
|
||||
|
||||
@@ -81,15 +81,39 @@ slowcat(d3, f3, [a3, c3].euclid(3, 4, 1), g3.fast(2))
|
||||
|
||||
Here is a short description of all the functions used:
|
||||
|
||||
- slowcat: play elements sequentially, where each lasts one cycle
|
||||
- brackets: elements inside brackets are divided equally over the time of their parent
|
||||
- euclid(p, s, o): place p pulses evenly over s steps, with offset o [@godfried]
|
||||
- fast(n): speed up by n. `g3.fast(2)` will play g3 two times.
|
||||
- off(n, f): copy each event, offset it by n cycles and apply function f
|
||||
- legato(n): multiply duration of event with n
|
||||
- echo(t, n, v): copy each event t times, with n cycles in between each copy, decreasing velocity by v
|
||||
- tone(instrument): play back each event with the given Tone.js instrument
|
||||
- pianoroll(): visualize events as midi notes in a pianoroll
|
||||
- `slowcat`: play elements sequentially, where each lasts one cycle
|
||||
- `brackets`: elements inside brackets are divided equally over the time of their parent
|
||||
- `euclid(p, s, o)`: place p pulses evenly over s steps, with offset o [@toussaintEuclideanAlgorithmGenerates2005]
|
||||
- `fast(n)`: speed up by n. `g3.fast(2)` will play g3 two times.
|
||||
- `off(n, f)`: copy each event, offset it by n cycles and apply function f
|
||||
- `legato(n)`: multiply duration of event with n
|
||||
- `echo(t, n, v)`: copy each event t times, with n cycles in between each copy, decreasing velocity by v
|
||||
- `tone(instrument)`: play back each event with the given Tone.js instrument
|
||||
- `pianoroll()`: visualize events as midi notes in a pianoroll
|
||||
|
||||
# Ways to make Sound
|
||||
|
||||
To generate sound, Strudel supports different outputs:
|
||||
|
||||
- Tone.js
|
||||
- Web Audio API
|
||||
- WebDirt, a js recreation of Tidal's *Dirt* sample engine
|
||||
- OSC via osc-js
|
||||
- MIDI via WebMIDI
|
||||
|
||||
Tone.js proved to be limited for the use case of Strudel, where each individual event could potentially have a completely different audio graph.
|
||||
While the Web Audio API takes a *fire-and-forget* approach, creating a lot of Tone.js instruments and effects causes performance issues quickly. For that reason, we chose to search for alternatives.
|
||||
|
||||
Strudel's Web Audio API output creates a new audio graph for each event. It currently supports basic oscillators, sample playback, envelopes, filters and
|
||||
an experimental support for soundfonts.
|
||||
|
||||
WebDirt [@ogbornDktr0WebDirt2022] was created as part of the Estuary Live Coding System [@ogbornEstuaryBrowserbasedCollaborative2017], and proved to be a solid choice for handling samples in Strudel as well.
|
||||
|
||||
Using OSC, it is possible to send messages to SuperDirt [@SuperDirt2022], which is what Tidal does to generate sound.
|
||||
The downside of using OSC is that it requires the user to install SuperCollider and its sc3plugins library, which can be difficult.
|
||||
|
||||
The MIDI output can be used to send MIDI messages to either external instruments or to other programs on the same device.
|
||||
Web MIDI is currently only supported on Chromium-based browsers.
|
||||
|
||||
# Future Outlook
|
||||
|
||||
@@ -99,42 +123,15 @@ The project is still young, with many features on the horizon. As general guidin
|
||||
2. consistent with Tidal's approach to pattern
|
||||
3. modular and extensible
|
||||
|
||||
The main accessibility advantage over Tidal is the zero install browser environment. It is not yet accessible to screen reader users, but will be soon with the integration of the CodeMirror 6 editor. While Strudel can control Tidal's SuperDirt audio system via OSC, it requires the user to install SuperCollider and its sc3plugins library, which can be difficult. Without SuperDirt, Strudel is able to output sound itself via Tone.js, however this is limited both in terms of available features and runtime performance. For the future, it is planned to integrate alternative sound engines such as glicol [@glicol] and faust [@faust]. To improve compatibility with Tidal, more Tidal functions are planned to be ported, as well as full compatibility with SuperDirt. Besides sound, other ways to render events are being explored, such as graphical, and choreographic output. We are also looking into alternative ways of editing patterns, including multi-user editing for network music, parsing a novel syntax to escape the constraints of javascript, and developing hardware/e-textile interfaces.
|
||||
For the future, it is planned to integrate alternative sound engines such as Glicol [@lanChaosprintGlicol2022] and Faust [@FaustProgrammingLanguage2022]. To improve compatibility with Tidal, more Tidal functions are planned to be ported, as well as full compatibility with SuperDirt. Besides sound, other ways to render events are being explored, such as graphical, and choreographic output. We are also looking into alternative ways of editing patterns, including multi-user editing for network music, parsing a novel syntax to escape the constraints of javascript, and developing hardware/e-textile interfaces.
|
||||
|
||||
# Links
|
||||
|
||||
The Strudel REPL is available at <https://strudel.tidalcycles.org>, including an interactive tutorial.
|
||||
The repository is at <https://github.com/tidalcycles/strudel>, all the code is open source under the GPL-3.0 License.
|
||||
|
||||
# Technical requirements
|
||||
|
||||
- Space for one laptop + small audio interface (20 cm x 20cm), with mains power.
|
||||
- Stereo sound system, either placed behind presenter (for direct monitoring) or with additional stereo monitors.
|
||||
- Audio from audio interface: stereo pair 6,3mm jack outputs (balanced)
|
||||
- Projector / screen (HDMI.)
|
||||
|
||||
# Acknowledgments
|
||||
|
||||
Thanks to the Strudel and wider Tidal, live coding, webaudio and free/open source software communities for inspiration and support. Alex McLean's work on this project is supported by a UKRI Future Leaders Fellowship [grant number MR/V025260/1].
|
||||
|
||||
# References
|
||||
|
||||
[@roberts2016]: https://www.tandfonline.com/doi/abs/10.1080/14794713.2016.1227602?journalCode=rpdm20
|
||||
[@gibber]: https://quod.lib.umich.edu/i/icmc/bbp2372.2012.011/2/--gibber-live-coding-audio-in-the-browser?page=root;size=150;view=text
|
||||
[@alternate-timelines]: https://zenodo.org/record/5788732
|
||||
[@tidal.pegjs]: https://www.semanticscholar.org/paper/Bringing-the-TidalCycles-Mini-Notation-to-the-Roberts/74965efadd572ae3f40d14c633a5c8581c1b9f42
|
||||
[@tidalvortex]: https://zenodo.org/record/6456380
|
||||
[@estuary]: https://www.semanticscholar.org/paper/Estuary%3A-Browser-based-Collaborative-Projectional-Ogborn-Beverley/c6b5d34575d6230dfd8751ca4af8e5f6e44d916b
|
||||
[@tidalcycles]: https://dl.acm.org/doi/10.1145/2633638.2633647
|
||||
[@hession]: https://www.scopus.com/record/display.uri?eid=2-s2.0-84907386880&origin=inward&txGid=03307e26fba02a27bdc68bda462016f6266316467_Extending_Instruments_with_Live_Algorithms_in_a_Percussion_Code_Duo
|
||||
[@spiegel]: https://www.academia.edu/664807/Manipulations_of_musical_patterns
|
||||
[@bel]: https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.517.7129
|
||||
[@algorithmicpattern]: https://zenodo.org/record/4299661
|
||||
[@fabricating]: https://zenodo.org/record/2155745
|
||||
[@cyclic-patterns]: https://zenodo.org/record/1548969
|
||||
[@feedforward]: https://zenodo.org/record/6353969
|
||||
[@godfried]: https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.72.1340
|
||||
[@glicol]: https://webaudioconf.com/posts/2021_8/
|
||||
[@faust]: https://webaudioconf.com/posts/2019_38/
|
||||
[@wags]: https://mikesol.github.io/purescript-wags/
|
||||
[@hydra]: https://hydra.ojack.xyz/docs/#/
|
||||
Binary file not shown.
+2
-2
@@ -8,10 +8,10 @@ fi
|
||||
|
||||
pandoc -s demo.md \
|
||||
--from markdown+auto_identifiers --pdf-engine=xelatex --template tex/latex-template.tex -V colorlinks --number-sections \
|
||||
--filter=pandoc-url2cite --citeproc --pdf-engine=xelatex \
|
||||
--citeproc --pdf-engine=xelatex \
|
||||
--dpi=300 -o demo.pdf
|
||||
|
||||
pandoc -s demo.md --filter bin/code-filter.py --filter=pandoc-url2cite \
|
||||
pandoc -s demo.md --filter bin/code-filter.py \
|
||||
--citeproc \
|
||||
-t markdown-citations -t markdown-fenced_divs \
|
||||
-o demo-preprocessed.md
|
||||
|
||||
+2
-1
@@ -23,4 +23,5 @@ dist-ssr
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
oldtunes.mjs
|
||||
oldtunes.mjs
|
||||
public/samples/EMU World
|
||||
Generated
+506
-10
@@ -8,6 +8,8 @@
|
||||
"name": "@strudel.cycles/repl",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@supabase/supabase-js": "^1.35.3",
|
||||
"nanoid": "^4.0.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
},
|
||||
@@ -509,6 +511,59 @@
|
||||
"node": ">= 8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/functions-js": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-1.3.3.tgz",
|
||||
"integrity": "sha512-35vO9niHRtzGe1QSvXKdOfvGPiX2KC44dGpWU6y0/gZCfTIgog/soU9HqABzQC/maVowO3hGLWfez5aN0MKfow==",
|
||||
"dependencies": {
|
||||
"cross-fetch": "^3.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/gotrue-js": {
|
||||
"version": "1.22.16",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-1.22.16.tgz",
|
||||
"integrity": "sha512-t/tR87anihvyc9ePk53NF2CG8yzTDiWOt0ReZpr7sRKV91sHNLbghVMsMDwPONWnVQ9V2/qfKutuAvPGTB/Dew==",
|
||||
"dependencies": {
|
||||
"cross-fetch": "^3.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/postgrest-js": {
|
||||
"version": "0.37.3",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-0.37.3.tgz",
|
||||
"integrity": "sha512-Iqwv9D80YGkCi60Zl2lsV04WxOmjFqxpFmxoZ5xUqsVZHvIoAbJ9tMM3+0F7VkZZbsjnaYHrpJ58xLxU7cgy2w==",
|
||||
"dependencies": {
|
||||
"cross-fetch": "^3.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/realtime-js": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-1.7.2.tgz",
|
||||
"integrity": "sha512-DMUaFIKj7KszGtWTTQbhMmUzZf7UnwYqySsmY+G8HgYxvY3ZaVa+DZD0I6ofgr4OLNr0po/ODM2a4lf5m5GNBg==",
|
||||
"dependencies": {
|
||||
"@types/phoenix": "^1.5.4",
|
||||
"websocket": "^1.0.34"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/storage-js": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-1.7.1.tgz",
|
||||
"integrity": "sha512-0ZlKlGgB3Qzhu07fts5pr2Y+c4xW58d7sDOB9m6nSfltS6SSfBPmqtdhU0zV9vy3vAjC74XeqUcpMhHP5E+aoA==",
|
||||
"dependencies": {
|
||||
"cross-fetch": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/supabase-js": {
|
||||
"version": "1.35.3",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-1.35.3.tgz",
|
||||
"integrity": "sha512-uwO8OVdMFsGZNZ1xQhFz22+PSW0EWYZ5xVq+jQeGz8nhabEu+Q9Uyep/bcNzOpyPJRzbGfxSPRzgAdAxfJgFhw==",
|
||||
"dependencies": {
|
||||
"@supabase/functions-js": "^1.3.3",
|
||||
"@supabase/gotrue-js": "^1.22.14",
|
||||
"@supabase/postgrest-js": "^0.37.2",
|
||||
"@supabase/realtime-js": "^1.7.2",
|
||||
"@supabase/storage-js": "^1.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/typography": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.2.tgz",
|
||||
@@ -523,6 +578,11 @@
|
||||
"tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || insiders"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/phoenix": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.5.4.tgz",
|
||||
"integrity": "sha512-L5eZmzw89eXBKkiqVBcJfU1QGx9y+wurRIEgt0cuLH0hwNtVUxtx+6cu0R2STwWj468sjXyBYPYDtGclUd1kjQ=="
|
||||
},
|
||||
"node_modules/@vitejs/plugin-react": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-1.3.2.tgz",
|
||||
@@ -688,6 +748,18 @@
|
||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||
}
|
||||
},
|
||||
"node_modules/bufferutil": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.6.tgz",
|
||||
"integrity": "sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/camelcase-css": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
||||
@@ -790,6 +862,14 @@
|
||||
"safe-buffer": "~5.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-fetch": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
|
||||
"integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
|
||||
"dependencies": {
|
||||
"node-fetch": "2.6.7"
|
||||
}
|
||||
},
|
||||
"node_modules/cssesc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
@@ -802,6 +882,15 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/d": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
|
||||
"integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
|
||||
"dependencies": {
|
||||
"es5-ext": "^0.10.50",
|
||||
"type": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
@@ -860,6 +949,39 @@
|
||||
"integrity": "sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/es5-ext": {
|
||||
"version": "0.10.61",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz",
|
||||
"integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"es6-iterator": "^2.0.3",
|
||||
"es6-symbol": "^3.1.3",
|
||||
"next-tick": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-iterator": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
|
||||
"integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
|
||||
"dependencies": {
|
||||
"d": "1",
|
||||
"es5-ext": "^0.10.35",
|
||||
"es6-symbol": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-symbol": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
|
||||
"integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
|
||||
"dependencies": {
|
||||
"d": "^1.0.1",
|
||||
"ext": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.14.39",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.39.tgz",
|
||||
@@ -1239,6 +1361,19 @@
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ext": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz",
|
||||
"integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==",
|
||||
"dependencies": {
|
||||
"type": "^2.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ext/node_modules/type": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz",
|
||||
"integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ=="
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.2.11",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
|
||||
@@ -1426,6 +1561,11 @@
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@@ -1528,15 +1668,48 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"dev": true,
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.0.tgz",
|
||||
"integrity": "sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg==",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
"node": "^14 || ^16 || >=18"
|
||||
}
|
||||
},
|
||||
"node_modules/next-tick": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
|
||||
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp-build": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz",
|
||||
"integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==",
|
||||
"bin": {
|
||||
"node-gyp-build": "bin.js",
|
||||
"node-gyp-build-optional": "optional.js",
|
||||
"node-gyp-build-test": "build-test.js"
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
@@ -1714,6 +1887,18 @@
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/postcss/node_modules/nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
@@ -1980,6 +2165,36 @@
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"node_modules/type": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
|
||||
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
|
||||
},
|
||||
"node_modules/typedarray-to-buffer": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
|
||||
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
|
||||
"dependencies": {
|
||||
"is-typedarray": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/utf-8-validate": {
|
||||
"version": "5.0.9",
|
||||
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.9.tgz",
|
||||
"integrity": "sha512-Yek7dAy0v3Kl0orwMlvi7TPtiCNrdfHNd7Gcc/pLq4BLXqfAmd0J7OWMizUQnTTJsyjKn02mU7anqwfmUP4J8Q==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
@@ -2023,6 +2238,49 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"node_modules/websocket": {
|
||||
"version": "1.0.34",
|
||||
"resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz",
|
||||
"integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==",
|
||||
"dependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"debug": "^2.2.0",
|
||||
"es5-ext": "^0.10.50",
|
||||
"typedarray-to-buffer": "^3.1.5",
|
||||
"utf-8-validate": "^5.0.2",
|
||||
"yaeti": "^0.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/websocket/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/websocket/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
@@ -2032,6 +2290,14 @@
|
||||
"node": ">=0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/yaeti": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
|
||||
"integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==",
|
||||
"engines": {
|
||||
"node": ">=0.10.32"
|
||||
}
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||
@@ -2402,6 +2668,59 @@
|
||||
"picomatch": "^2.2.2"
|
||||
}
|
||||
},
|
||||
"@supabase/functions-js": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-1.3.3.tgz",
|
||||
"integrity": "sha512-35vO9niHRtzGe1QSvXKdOfvGPiX2KC44dGpWU6y0/gZCfTIgog/soU9HqABzQC/maVowO3hGLWfez5aN0MKfow==",
|
||||
"requires": {
|
||||
"cross-fetch": "^3.1.5"
|
||||
}
|
||||
},
|
||||
"@supabase/gotrue-js": {
|
||||
"version": "1.22.16",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-1.22.16.tgz",
|
||||
"integrity": "sha512-t/tR87anihvyc9ePk53NF2CG8yzTDiWOt0ReZpr7sRKV91sHNLbghVMsMDwPONWnVQ9V2/qfKutuAvPGTB/Dew==",
|
||||
"requires": {
|
||||
"cross-fetch": "^3.0.6"
|
||||
}
|
||||
},
|
||||
"@supabase/postgrest-js": {
|
||||
"version": "0.37.3",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-0.37.3.tgz",
|
||||
"integrity": "sha512-Iqwv9D80YGkCi60Zl2lsV04WxOmjFqxpFmxoZ5xUqsVZHvIoAbJ9tMM3+0F7VkZZbsjnaYHrpJ58xLxU7cgy2w==",
|
||||
"requires": {
|
||||
"cross-fetch": "^3.1.5"
|
||||
}
|
||||
},
|
||||
"@supabase/realtime-js": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-1.7.2.tgz",
|
||||
"integrity": "sha512-DMUaFIKj7KszGtWTTQbhMmUzZf7UnwYqySsmY+G8HgYxvY3ZaVa+DZD0I6ofgr4OLNr0po/ODM2a4lf5m5GNBg==",
|
||||
"requires": {
|
||||
"@types/phoenix": "^1.5.4",
|
||||
"websocket": "^1.0.34"
|
||||
}
|
||||
},
|
||||
"@supabase/storage-js": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-1.7.1.tgz",
|
||||
"integrity": "sha512-0ZlKlGgB3Qzhu07fts5pr2Y+c4xW58d7sDOB9m6nSfltS6SSfBPmqtdhU0zV9vy3vAjC74XeqUcpMhHP5E+aoA==",
|
||||
"requires": {
|
||||
"cross-fetch": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"@supabase/supabase-js": {
|
||||
"version": "1.35.3",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-1.35.3.tgz",
|
||||
"integrity": "sha512-uwO8OVdMFsGZNZ1xQhFz22+PSW0EWYZ5xVq+jQeGz8nhabEu+Q9Uyep/bcNzOpyPJRzbGfxSPRzgAdAxfJgFhw==",
|
||||
"requires": {
|
||||
"@supabase/functions-js": "^1.3.3",
|
||||
"@supabase/gotrue-js": "^1.22.14",
|
||||
"@supabase/postgrest-js": "^0.37.2",
|
||||
"@supabase/realtime-js": "^1.7.2",
|
||||
"@supabase/storage-js": "^1.7.0"
|
||||
}
|
||||
},
|
||||
"@tailwindcss/typography": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.2.tgz",
|
||||
@@ -2413,6 +2732,11 @@
|
||||
"lodash.merge": "^4.6.2"
|
||||
}
|
||||
},
|
||||
"@types/phoenix": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.5.4.tgz",
|
||||
"integrity": "sha512-L5eZmzw89eXBKkiqVBcJfU1QGx9y+wurRIEgt0cuLH0hwNtVUxtx+6cu0R2STwWj468sjXyBYPYDtGclUd1kjQ=="
|
||||
},
|
||||
"@vitejs/plugin-react": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-1.3.2.tgz",
|
||||
@@ -2519,6 +2843,14 @@
|
||||
"picocolors": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"bufferutil": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.6.tgz",
|
||||
"integrity": "sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw==",
|
||||
"requires": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
}
|
||||
},
|
||||
"camelcase-css": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
||||
@@ -2593,12 +2925,29 @@
|
||||
"safe-buffer": "~5.1.1"
|
||||
}
|
||||
},
|
||||
"cross-fetch": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
|
||||
"integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
|
||||
"requires": {
|
||||
"node-fetch": "2.6.7"
|
||||
}
|
||||
},
|
||||
"cssesc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"d": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
|
||||
"integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
|
||||
"requires": {
|
||||
"es5-ext": "^0.10.50",
|
||||
"type": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
@@ -2643,6 +2992,35 @@
|
||||
"integrity": "sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==",
|
||||
"dev": true
|
||||
},
|
||||
"es5-ext": {
|
||||
"version": "0.10.61",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz",
|
||||
"integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==",
|
||||
"requires": {
|
||||
"es6-iterator": "^2.0.3",
|
||||
"es6-symbol": "^3.1.3",
|
||||
"next-tick": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"es6-iterator": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
|
||||
"integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
|
||||
"requires": {
|
||||
"d": "1",
|
||||
"es5-ext": "^0.10.35",
|
||||
"es6-symbol": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"es6-symbol": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
|
||||
"integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
|
||||
"requires": {
|
||||
"d": "^1.0.1",
|
||||
"ext": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"esbuild": {
|
||||
"version": "0.14.39",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.39.tgz",
|
||||
@@ -2829,6 +3207,21 @@
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"dev": true
|
||||
},
|
||||
"ext": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz",
|
||||
"integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==",
|
||||
"requires": {
|
||||
"type": "^2.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"type": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz",
|
||||
"integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"fast-glob": {
|
||||
"version": "3.2.11",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
|
||||
@@ -2965,6 +3358,11 @@
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true
|
||||
},
|
||||
"is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@@ -3043,10 +3441,27 @@
|
||||
"dev": true
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"dev": true
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.0.tgz",
|
||||
"integrity": "sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg=="
|
||||
},
|
||||
"next-tick": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
|
||||
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"requires": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node-gyp-build": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz",
|
||||
"integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ=="
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "2.0.4",
|
||||
@@ -3104,6 +3519,14 @@
|
||||
"nanoid": "^3.3.3",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"postcss-js": {
|
||||
@@ -3325,6 +3748,32 @@
|
||||
"is-number": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"type": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
|
||||
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
|
||||
},
|
||||
"typedarray-to-buffer": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
|
||||
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
|
||||
"requires": {
|
||||
"is-typedarray": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"version": "5.0.9",
|
||||
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.9.tgz",
|
||||
"integrity": "sha512-Yek7dAy0v3Kl0orwMlvi7TPtiCNrdfHNd7Gcc/pLq4BLXqfAmd0J7OWMizUQnTTJsyjKn02mU7anqwfmUP4J8Q==",
|
||||
"requires": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
}
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
@@ -3344,12 +3793,59 @@
|
||||
"rollup": "^2.59.0"
|
||||
}
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"websocket": {
|
||||
"version": "1.0.34",
|
||||
"resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz",
|
||||
"integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==",
|
||||
"requires": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"debug": "^2.2.0",
|
||||
"es5-ext": "^0.10.50",
|
||||
"typedarray-to-buffer": "^3.1.5",
|
||||
"utf-8-validate": "^5.0.2",
|
||||
"yaeti": "^0.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"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=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"requires": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||
"dev": true
|
||||
},
|
||||
"yaeti": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
|
||||
"integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug=="
|
||||
},
|
||||
"yaml": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||
|
||||
+3
-1
@@ -7,7 +7,7 @@
|
||||
"start": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"test": "mocha ./src/test --colors",
|
||||
"test": "NODE_OPTIONS='--experimental-fetch' mocha ./src/test --colors",
|
||||
"snapshot": "cd ./src/ && rm -f ./tunes.snapshot.mjs && node ./shoot.mjs > ./tunes.snapshot.mjs",
|
||||
"add-license": "cat etc/agpl-header.txt ../docs/static/js/*LICENSE.txt > /tmp/strudel-license.txt && cp /tmp/strudel-license.txt ../docs/static/js/*LICENSE.txt",
|
||||
"predeploy": "npm run build",
|
||||
@@ -15,6 +15,8 @@
|
||||
"static": "npx serve ../docs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@supabase/supabase-js": "^1.35.3",
|
||||
"nanoid": "^4.0.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
},
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+83
-11
@@ -6,15 +6,24 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
|
||||
import controls from '@strudel.cycles/core/controls.mjs';
|
||||
import { evalScope, evaluate } from '@strudel.cycles/eval';
|
||||
import { CodeMirror, cx, useHighlighting, useRepl, useWebMidi } from '@strudel.cycles/react';
|
||||
import { CodeMirror, cx, flash, useHighlighting, useRepl, useWebMidi } from '@strudel.cycles/react';
|
||||
import { getDefaultSynth, cleanupDraw, cleanupUi, Tone } from '@strudel.cycles/tone';
|
||||
import React, { useCallback, useLayoutEffect, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||
import './App.css';
|
||||
import logo from './logo.svg';
|
||||
import * as tunes from './tunes.mjs';
|
||||
import { prebake } from './prebake.mjs';
|
||||
import * as WebDirt from 'WebDirt';
|
||||
import { loadWebDirt } from '@strudel.cycles/webdirt';
|
||||
import { resetLoadedSamples, getAudioContext } from '@strudel.cycles/webaudio';
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
// Create a single supabase client for interacting with your database
|
||||
const supabase = createClient(
|
||||
'https://pidxdsxphlhzjnzmifth.supabase.co',
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM',
|
||||
);
|
||||
|
||||
evalScope(
|
||||
Tone,
|
||||
@@ -30,6 +39,7 @@ evalScope(
|
||||
import('@strudel.cycles/osc'),
|
||||
import('@strudel.cycles/webdirt'),
|
||||
import('@strudel.cycles/serial'),
|
||||
import('@strudel.cycles/soundfonts'),
|
||||
);
|
||||
|
||||
loadWebDirt({
|
||||
@@ -37,13 +47,37 @@ loadWebDirt({
|
||||
sampleFolder: 'EmuSP12',
|
||||
});
|
||||
|
||||
const initialUrl = window.location.href;
|
||||
const codeParam = window.location.href.split('#')[1];
|
||||
let decoded;
|
||||
try {
|
||||
decoded = atob(decodeURIComponent(codeParam || ''));
|
||||
} catch (err) {
|
||||
console.warn('failed to decode', err);
|
||||
prebake();
|
||||
|
||||
async function initCode() {
|
||||
// load code from url hash (either short hash from database or decode long hash)
|
||||
try {
|
||||
const initialUrl = window.location.href;
|
||||
const hash = initialUrl.split('?')[1]?.split('#')?.[0];
|
||||
const codeParam = window.location.href.split('#')[1];
|
||||
// looking like https://strudel.tidalcycles.org/?J01s5i1J0200 (fixed hash length)
|
||||
if (codeParam) {
|
||||
console.log('decode hash from url');
|
||||
// looking like https://strudel.tidalcycles.org/#ImMzIGUzIg%3D%3D (hash length depends on code length)
|
||||
return atob(decodeURIComponent(codeParam || ''));
|
||||
} else if (hash) {
|
||||
return supabase
|
||||
.from('code')
|
||||
.select('code')
|
||||
.eq('hash', hash)
|
||||
.then(({ data, error }) => {
|
||||
if (error) {
|
||||
console.warn('failed to load hash', err);
|
||||
}
|
||||
if (data.length) {
|
||||
console.log('load hash from database', hash);
|
||||
return data[0].code;
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('failed to decode', err);
|
||||
}
|
||||
}
|
||||
|
||||
function getRandomTune() {
|
||||
@@ -58,6 +92,7 @@ const isEmbedded = window.location !== window.parent.location;
|
||||
function App() {
|
||||
// const [editor, setEditor] = useState();
|
||||
const [view, setView] = useState();
|
||||
const [lastShared, setLastShared] = useState();
|
||||
const {
|
||||
setCode,
|
||||
setPattern,
|
||||
@@ -74,9 +109,12 @@ function App() {
|
||||
pushLog,
|
||||
pending,
|
||||
} = useRepl({
|
||||
tune: decoded || randomTune,
|
||||
tune: '// LOADING...',
|
||||
defaultSynth,
|
||||
});
|
||||
useEffect(() => {
|
||||
initCode().then((decoded) => setCode(decoded || randomTune));
|
||||
}, []);
|
||||
const logBox = useRef();
|
||||
// scroll log box to bottom when log changes
|
||||
useLayoutEffect(() => {
|
||||
@@ -92,6 +130,7 @@ function App() {
|
||||
if (e.ctrlKey || e.altKey) {
|
||||
if (e.code === 'Enter') {
|
||||
e.preventDefault();
|
||||
flash(view);
|
||||
await activateCode();
|
||||
} else if (e.code === 'Period') {
|
||||
cycle.stop();
|
||||
@@ -101,7 +140,7 @@ function App() {
|
||||
};
|
||||
window.addEventListener('keydown', handleKeyPress, true);
|
||||
return () => window.removeEventListener('keydown', handleKeyPress, true);
|
||||
}, [pattern, code, activateCode, cycle]);
|
||||
}, [pattern, code, activateCode, cycle, view]);
|
||||
|
||||
useHighlighting({ view, pattern, active: cycle.started && !activeCode?.includes('strudel disable-highlighting') });
|
||||
|
||||
@@ -182,6 +221,7 @@ function App() {
|
||||
cleanupDraw();
|
||||
cleanupUi();
|
||||
resetLoadedSamples();
|
||||
prebake();
|
||||
const parsed = await evaluate(_code);
|
||||
setPattern(parsed.pattern);
|
||||
setActiveCode(_code);
|
||||
@@ -195,6 +235,38 @@ function App() {
|
||||
<a href="./tutorial">📚 tutorial</a>
|
||||
</button>
|
||||
)}
|
||||
{!isEmbedded && (
|
||||
<button
|
||||
className={cx('cursor-pointer hover:bg-gray-300', !isEmbedded ? 'p-2' : 'px-2')}
|
||||
onClick={async () => {
|
||||
const codeToShare = activeCode || code;
|
||||
if (lastShared === codeToShare) {
|
||||
// alert('Link already generated!');
|
||||
pushLog(`Link already generated!`);
|
||||
return;
|
||||
}
|
||||
// generate uuid in the browser
|
||||
const hash = nanoid(12);
|
||||
const { data, error } = await supabase.from('code').insert([{ code: codeToShare, hash }]);
|
||||
if (!error) {
|
||||
setLastShared(activeCode || code);
|
||||
const shareUrl = window.location.origin + '?' + hash;
|
||||
// copy shareUrl to clipboard
|
||||
navigator.clipboard.writeText(shareUrl);
|
||||
const message = `Link copied to clipboard: ${shareUrl}`;
|
||||
// alert(message);
|
||||
pushLog(message);
|
||||
} else {
|
||||
console.log('error', error);
|
||||
const message = `Error: ${error.message}`;
|
||||
// alert(message);
|
||||
pushLog(message);
|
||||
}
|
||||
}}
|
||||
>
|
||||
📣 share{lastShared && lastShared === (activeCode || code) ? 'd!' : ''}
|
||||
</button>
|
||||
)}
|
||||
{isEmbedded && (
|
||||
<button className={cx('hover:bg-gray-300 px-2')}>
|
||||
<a href={window.location.href} target="_blank" rel="noopener noreferrer" title="Open in REPL">
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { Pattern, toMidi } from '@strudel.cycles/core';
|
||||
import { samples } from '@strudel.cycles/webaudio';
|
||||
|
||||
export function prebake() {
|
||||
samples(
|
||||
{
|
||||
piano: {
|
||||
A0: 'A0v8.mp3',
|
||||
C1: 'C1v8.mp3',
|
||||
Ds1: 'Ds1v8.mp3',
|
||||
Fs1: 'Fs1v8.mp3',
|
||||
A1: 'A1v8.mp3',
|
||||
C2: 'C2v8.mp3',
|
||||
Ds2: 'Ds2v8.mp3',
|
||||
Fs2: 'Fs2v8.mp3',
|
||||
A2: 'A2v8.mp3',
|
||||
C3: 'C3v8.mp3',
|
||||
Ds3: 'Ds3v8.mp3',
|
||||
Fs3: 'Fs3v8.mp3',
|
||||
A3: 'A3v8.mp3',
|
||||
C4: 'C4v8.mp3',
|
||||
Ds4: 'Ds4v8.mp3',
|
||||
Fs4: 'Fs4v8.mp3',
|
||||
A4: 'A4v8.mp3',
|
||||
C5: 'C5v8.mp3',
|
||||
Ds4: 'Ds4v8.mp3',
|
||||
Fs5: 'Fs5v8.mp3',
|
||||
A5: 'A5v8.mp3',
|
||||
C6: 'C6v8.mp3',
|
||||
Ds6: 'Ds6v8.mp3',
|
||||
Fs6: 'Fs6v8.mp3',
|
||||
A6: 'A6v8.mp3',
|
||||
C7: 'C7v8.mp3',
|
||||
Ds7: 'Ds7v8.mp3',
|
||||
Fs7: 'Fs7v8.mp3',
|
||||
A7: 'A7v8.mp3',
|
||||
C8: 'C8v8.mp3',
|
||||
},
|
||||
},
|
||||
// https://archive.org/details/SalamanderGrandPianoV3
|
||||
// License: CC-by http://creativecommons.org/licenses/by/3.0/ Author: Alexander Holm
|
||||
'./piano/',
|
||||
);
|
||||
fetch('EmuSP12.json')
|
||||
.then(res => res.json())
|
||||
.then(json => samples(json, './EmuSP12/'));
|
||||
}
|
||||
|
||||
const maxPan = toMidi('C8');
|
||||
const panwidth = (pan, width) => pan * width + (1 - width) / 2;
|
||||
|
||||
Pattern.prototype.piano = function () {
|
||||
return this.clip(1)
|
||||
.s('piano')
|
||||
.fmap((value) => {
|
||||
// pan by pitch
|
||||
const pan = panwidth(Math.min(toMidi(value.note) / maxPan, 1), 0.5);
|
||||
return { ...value, pan: (value.pan || 1) * pan };
|
||||
});
|
||||
};
|
||||
@@ -31,6 +31,8 @@ import '@strudel.cycles/xen/xen.mjs';
|
||||
// import '@strudel.cycles/serial/serial.mjs';
|
||||
// import controls from '@strudel.cycles/core/controls.mjs';
|
||||
|
||||
import { prebake } from './prebake.mjs';
|
||||
|
||||
class MockedNode {
|
||||
chain() {
|
||||
return this;
|
||||
@@ -121,6 +123,7 @@ const uiHelpersMocked = {
|
||||
backgroundImage: id,
|
||||
};
|
||||
|
||||
prebake();
|
||||
|
||||
// TODO: refactor to evalScope
|
||||
extend(
|
||||
|
||||
+151
-38
@@ -444,7 +444,7 @@ export const barryHarris = `backgroundImage(
|
||||
.scale('C bebop major')
|
||||
.transpose("<0 1 2 1>/8")
|
||||
.slow(2)
|
||||
.tone((await piano()).toDestination())
|
||||
.note().piano().out()
|
||||
`;
|
||||
|
||||
export const blippyRhodes = `const delay = new FeedbackDelay(1/12, .4).chain(vol(0.3), out());
|
||||
@@ -551,7 +551,7 @@ export const risingEnemy = `stack(
|
||||
.transpose("<0 1 2 1>/2".early(0.5))
|
||||
.transpose(5)
|
||||
.fast(2 / 3)
|
||||
.tone((await piano()).toDestination())`;
|
||||
.note().piano().out()`;
|
||||
|
||||
export const festivalOfFingers = `const chords = "<Cm7 Fm7 G7 F#7>";
|
||||
stack(
|
||||
@@ -563,7 +563,7 @@ stack(
|
||||
.scaleTranspose("0 4 0 6".early(".125 .5")).layer(scaleTranspose("0,<2 [4,6] [5,7]>/4"))
|
||||
).slow(2)
|
||||
.velocity(sine.struct("x*8").add(3/5).mul(2/5).fast(8))
|
||||
.tone((await piano()).chain(out()))`;
|
||||
.note().piano().out()`;
|
||||
|
||||
export const festivalOfFingers2 = `const chords = "<Cm7 Fm7 G7 F#7 >";
|
||||
const scales = cat('C minor','F dorian','G dorian','F# mixolydian')
|
||||
@@ -577,36 +577,35 @@ stack(
|
||||
).slow(2).transpose(-1)
|
||||
.legato(cosine.struct("x*8").add(4/5).mul(4/5).fast(8))
|
||||
.velocity(sine.struct("x*8").add(3/5).mul(2/5).fast(8))
|
||||
.tone((await piano()).chain(out())).fast(3/4)`;
|
||||
.note().piano().out().fast(3/4)`;
|
||||
|
||||
// iter, stut, stutWith
|
||||
// iter, echo, echoWith
|
||||
export const undergroundPlumber = `backgroundImage('https://images.nintendolife.com/news/2016/08/video_exploring_the_funky_inspiration_for_the_super_mario_bros_underground_theme/large.jpg',{ className:'darken' })
|
||||
|
||||
const drums = await players({
|
||||
bd: 'bd/BT0A0D0.wav',
|
||||
sn: 'sn/ST0T0S3.wav',
|
||||
hh: 'hh/000_hh3closedhh.wav',
|
||||
cp: 'cp/HANDCLP0.wav',
|
||||
samples({ bd: 'bd/BT0A0D0.wav', sn: 'sn/ST0T0S3.wav', hh: 'hh/000_hh3closedhh.wav', cp: 'cp/HANDCLP0.wav',
|
||||
}, 'https://loophole-letters.vercel.app/samples/tidal/')
|
||||
stack(
|
||||
"<<bd*2 bd> sn> hh".fast(4).slow(2).tone(drums.chain(vol(.5),out())),
|
||||
stack(
|
||||
"[c2 a1 bb1 ~] ~"
|
||||
.stut(2, .6, 1/16)
|
||||
.legato(.4)
|
||||
.slow(2)
|
||||
.tone(synth({...osc('sawtooth7'),...adsr(0,.3,0)}).chain(out())),
|
||||
"[g2,[c3 eb3]]".iter(4)
|
||||
.stutWith(4, 1/8, (x,n)=>x.transpose(n*12).velocity(Math.pow(.4,n)))
|
||||
.legato(.1)
|
||||
)
|
||||
.transpose("<0@2 5 0 7 5 0 -5>/2")
|
||||
|
||||
)
|
||||
.fast(2/3)
|
||||
.pianoroll({minMidi:21,maxMidi:180, background:'transparent',inactive:'#3F8F90',active:'#DE3123'})`;
|
||||
|
||||
export const bridgeIsOver = `const breaks = (await players({mad:'https://freesound.org/data/previews/22/22274_109943-lq.mp3'})).chain(out())
|
||||
const h = x=>x.transpose("<0@2 5 0 7 5 0 -5>/2")
|
||||
|
||||
stack(
|
||||
s("<<bd*2 bd> sn> hh").fast(2).gain(.7),
|
||||
"[c2 a1 bb1 ~] ~"
|
||||
.echo(2, 1/16, 1)
|
||||
.legato(.4)
|
||||
.slow(2)
|
||||
.layer(h)
|
||||
.note().s('square')
|
||||
.cutoff(400).decay(.12).sustain(0)
|
||||
,
|
||||
"[g2,[c3 eb3]]".iter(4)
|
||||
.echoWith(4, 1/8, (x,n)=>x.transpose(n*12).velocity(Math.pow(.4,n)))
|
||||
.legato(.1)
|
||||
.layer(h).note()
|
||||
).out()
|
||||
.fast(2/3)
|
||||
.pianoroll({})`;
|
||||
|
||||
export const bridgeIsOver = `samples({mad:'https://freesound.org/data/previews/22/22274_109943-lq.mp3'})
|
||||
stack(
|
||||
stack(
|
||||
"c3*2 [[c3@1.4 bb2] ab2] gb2*2 <[[gb2@1.4 ab2] bb2] gb2>".legato(".5 1".fast(2)).velocity(.8),
|
||||
@@ -618,9 +617,11 @@ stack(
|
||||
.velocity(.7)
|
||||
.legato(.5)
|
||||
.stut(3, .5, 1/8)
|
||||
).transpose(-1).tone((await piano()).chain(out())),
|
||||
"mad".slow(2).tone(breaks)
|
||||
).cpm(78).slow(4).pianoroll()
|
||||
).transpose(-1).note().piano(),
|
||||
s("mad").slow(2)
|
||||
).cpm(78).slow(4)
|
||||
.out()
|
||||
.pianoroll()
|
||||
`;
|
||||
|
||||
export const goodTimes = `const scale = cat('C3 dorian','Bb2 major').slow(4);
|
||||
@@ -637,10 +638,9 @@ stack(
|
||||
.legato("2")
|
||||
.scale(scale)
|
||||
.scaleTranspose("<0>".slow(4))
|
||||
.tone((await piano()).chain(out()))
|
||||
//.midi()
|
||||
.velocity(.8)
|
||||
.transpose(5)
|
||||
.note().piano().out()
|
||||
.velocity(.8)
|
||||
.slow(2)
|
||||
.pianoroll({maxMidi:100,minMidi:20})`;
|
||||
|
||||
@@ -651,7 +651,7 @@ export const echoPiano = `"<0 2 [4 6](3,4,1) 3*2>"
|
||||
.off(1/2, x=>x.scaleTranspose(6).color('steelblue'))
|
||||
.legato(.5)
|
||||
.echo(4, 1/8, .5)
|
||||
.tone((await piano()).chain(out()))
|
||||
.note().piano().out()
|
||||
.pianoroll()`;
|
||||
|
||||
export const sml1 = `
|
||||
@@ -687,7 +687,7 @@ stack(
|
||||
f3!2 e3!2 ab3!2 ~!2
|
||||
>\`
|
||||
.legato(.5)
|
||||
).fast(2) //.tone((await piano()).chain(out()))`;
|
||||
).fast(2) // .note().piano().out()`;
|
||||
|
||||
export const speakerman = `backgroundImage('https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fi.ytimg.com%2Fvi%2FXR0rKqW3VwY%2Fmaxresdefault.jpg&f=1&nofb=1',
|
||||
{ className:'darken', style:'background-size:cover'})
|
||||
@@ -827,7 +827,7 @@ export const festivalOfFingers3 = `"[-7*3],0,2,6,[8 7]"
|
||||
.scale(cat('D dorian','G mixolydian','C dorian','F mixolydian'))
|
||||
.legato(1)
|
||||
.slow(2)
|
||||
.tone((await piano()).toDestination())
|
||||
.note().piano().out()
|
||||
//.pianoroll({maxMidi:160})`;
|
||||
|
||||
export const bossa = `
|
||||
@@ -835,7 +835,7 @@ const scales = sequence('C minor', ['D locrian', 'G phrygian'], 'Bb2 minor', ['C
|
||||
stack(
|
||||
"<Cm7 [Dm7b5 G7b9] Bbm7 [Cm7b5 F7b9]>".fast(2).struct("x ~ x@3 x ~ x ~ ~ ~ x ~ x@3".late(1/8)).early(1/8).slow(2).voicings(),
|
||||
"[~ [0 ~]] 0 [~ [4 ~]] 4".sub(7).restart(scales).scale(scales).early(.25)
|
||||
).tone((await piano()).toDestination()).slow(2)`;
|
||||
).note().piano().out().slow(2)`;
|
||||
|
||||
export const customTrigger = `stack(
|
||||
freq("55 [110,165] 110 [220,275]".mul("<1 <3/4 2/3>>").struct("x(3,8)").layer(x=>x.mul("1.006,.995"))),
|
||||
@@ -906,3 +906,116 @@ stack(
|
||||
)
|
||||
.out()
|
||||
.slow(3/2)`;
|
||||
|
||||
export const swimmingWithSoundfonts = `stack(
|
||||
n(
|
||||
"~",
|
||||
"~",
|
||||
"~",
|
||||
"A5 [F5@2 C5] [D5@2 F5] F5",
|
||||
"[C5@2 F5] [F5@2 C6] A5 G5",
|
||||
"A5 [F5@2 C5] [D5@2 F5] F5",
|
||||
"[C5@2 F5] [Bb5 A5 G5] F5@2",
|
||||
"A5 [F5@2 C5] [D5@2 F5] F5",
|
||||
"[C5@2 F5] [F5@2 C6] A5 G5",
|
||||
"A5 [F5@2 C5] [D5@2 F5] F5",
|
||||
"[C5@2 F5] [Bb5 A5 G5] F5@2",
|
||||
"A5 [F5@2 C5] A5 F5",
|
||||
"Ab5 [F5@2 Ab5] G5@2",
|
||||
"A5 [F5@2 C5] A5 F5",
|
||||
"Ab5 [F5@2 C5] C6@2",
|
||||
"A5 [F5@2 C5] [D5@2 F5] F5",
|
||||
"[C5@2 F5] [Bb5 A5 G5] F5@2"
|
||||
).s('Sitar: Ethnic'),
|
||||
n(
|
||||
"[F4,Bb4,D5] [[D4,G4,Bb4]@2 [Bb3,D4,F4]] [[G3,C4,E4]@2 [[Ab3,F4] [A3,Gb4]]] [Bb3,E4,G4]",
|
||||
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, Bb3, Db3] [F3, Bb3, Db3]]",
|
||||
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
|
||||
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
|
||||
"[~ [A3, C4, E4] [A3, C4, E4]] [~ [Ab3, C4, Eb4] [Ab3, C4, Eb4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [G3, C4, E4] [G3, C4, E4]]",
|
||||
"[~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
|
||||
"[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]",
|
||||
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
|
||||
"[~ [A3, C4, E4] [A3, C4, E4]] [~ [Ab3, C4, Eb4] [Ab3, C4, Eb4]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [G3, C4, E4] [G3, C4, E4]]",
|
||||
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
|
||||
"[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]",
|
||||
"[~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [A3, C4, F4] [A3, C4, F4]] [~ [A3, C4, F4] [A3, C4, F4]]",
|
||||
"[~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [G3, Bb3, F4] [G3, Bb3, F4]] [~ [G3, Bb3, E4] [G3, Bb3, E4]]",
|
||||
"[~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [Bb3, D3, F4] [Bb3, D3, F4]] [~ [A3, C4, F4] [A3, C4, F4]] [~ [A3, C4, F4] [A3, C4, F4]]",
|
||||
"[~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [Ab3, B3, F4] [Ab3, B3, F4]] [~ [G3, Bb3, F4] [G3, Bb3, F4]] [~ [G3, Bb3, E4] [G3, Bb3, E4]]",
|
||||
"[~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, A3, C3] [F3, A3, C3]] [~ [F3, Bb3, D3] [F3, Bb3, D3]] [~ [F3, B3, D3] [F3, B3, D3]]",
|
||||
"[~ [F3, Bb3, D4] [F3, Bb3, D4]] [~ [F3, Bb3, C4] [F3, Bb3, C4]] [~ [F3, A3, C4] [F3, A3, C4]] [~ [F3, A3, C4] [F3, A3, C4]]"
|
||||
).s('Kalimba: Ethnic'),
|
||||
n(
|
||||
"[G3 G3 C3 E3]",
|
||||
"[F2 D2 G2 C2]",
|
||||
"[F2 D2 G2 C2]",
|
||||
"[F2 A2 Bb2 B2]",
|
||||
"[A2 Ab2 G2 C2]",
|
||||
"[F2 A2 Bb2 B2]",
|
||||
"[G2 C2 F2 F2]",
|
||||
"[F2 A2 Bb2 B2]",
|
||||
"[A2 Ab2 G2 C2]",
|
||||
"[F2 A2 Bb2 B2]",
|
||||
"[G2 C2 F2 F2]",
|
||||
"[Bb2 Bb2 A2 A2]",
|
||||
"[Ab2 Ab2 G2 [C2 D2 E2]]",
|
||||
"[Bb2 Bb2 A2 A2]",
|
||||
"[Ab2 Ab2 G2 [C2 D2 E2]]",
|
||||
"[F2 A2 Bb2 B2]",
|
||||
"[G2 C2 F2 F2]"
|
||||
).s('Acoustic Bass: Bass')
|
||||
).slow(51)
|
||||
.out()`;
|
||||
|
||||
export const outroMusic = `samples({
|
||||
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav','bd/BT0A0DA.wav','bd/BT0A0D3.wav','bd/BT0A0D0.wav','bd/BT0A0A7.wav'],
|
||||
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
|
||||
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
|
||||
perc: ['perc/002_perc2.wav'],
|
||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||
|
||||
"C^7 Am7 Dm7 G7".slow(2).voicings()
|
||||
.stack("0@6 [<1 2> <2 0> 1]@2".scale('C5 major'))
|
||||
.n().slow(4)
|
||||
.s('0040_FluidR3_GM_sf2_file')
|
||||
.color('steelblue')
|
||||
.stack(
|
||||
"<-7 ~@2 [~@2 -7] -9 ~@2 [~@2 -9] -10!2 ~ [~@2 -10] -5 ~ [-3 -2 -10]@2>*2".scale('C3 major')
|
||||
.n().s('sawtooth').color('brown')
|
||||
)
|
||||
.attack(0.05).decay(.1).sustain(.7)
|
||||
.cutoff(perlin.range(800,2000))
|
||||
.gain(.3)
|
||||
.stack(
|
||||
s("<bd!3 [bd ~ bd]> sd,hh*3,~@5 <perc perc*3>")
|
||||
.speed(perlin.range(.9,1.1))
|
||||
.n(3).color('gray')
|
||||
).slow(3/2)
|
||||
//.pianoroll({autorange:1,vertical:1,fold:0})
|
||||
.out()`;
|
||||
|
||||
export const bassFuge = `samples({ flbass: ['00_c2_finger_long_neck.wav','01_c2_finger_short_neck.wav','02_c2_finger_long_bridge.wav','03_c2_finger_short_bridge.wav','04_c2_pick_long.wav','05_c2_pick_short.wav','06_c2_palm_mute.wav'] },
|
||||
'github:cleary/samples-flbass/main/')
|
||||
samples({
|
||||
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav','bd/BT0A0DA.wav','bd/BT0A0D3.wav','bd/BT0A0D0.wav','bd/BT0A0A7.wav'],
|
||||
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
|
||||
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
|
||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||
|
||||
note("<8(3,8) <7 7*2> [4 5@3] 8>".sub(1) // sub 1 -> 1-indexed
|
||||
.layer(
|
||||
x=>x,
|
||||
x=>x.add(7).color('steelblue')
|
||||
.off(1/8,x=>x.add("2,4").off(1/8,x=>x.add(5).echo(4,.125,.5)))
|
||||
.slow(2),
|
||||
).scale('A1 minor'))
|
||||
.s("flbass").n(0)
|
||||
.gain(.3)
|
||||
.cutoff(sine.slow(7).range(200,4000))
|
||||
.resonance(10)
|
||||
//.hcutoff(400)
|
||||
.clip(1)
|
||||
.stack(s("bd:1*2,~ sd:0,[~ hh:0]*2"))
|
||||
.out()
|
||||
.pianoroll({vertical:1})`;
|
||||
|
||||
+14
-10
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
samples/old
|
||||
@@ -0,0 +1,24 @@
|
||||
const headlines = [
|
||||
`
|
||||
██████ ▄▄▄█████▓ ██▀███ █ ██ ▓█████▄ ▓█████ ██▓
|
||||
▒██ ▒ ▓ ██▒ ▓▒▓██ ▒ ██▒ ██ ▓██▒▒██▀ ██▌▓█ ▀ ▓██▒
|
||||
░ ▓██▄ ▒ ▓██░ ▒░▓██ ░▄█ ▒▓██ ▒██░░██ █▌▒███ ▒██░
|
||||
▒ ██▒░ ▓██▓ ░ ▒██▀▀█▄ ▓▓█ ░██░░▓█▄ ▌▒▓█ ▄ ▒██░
|
||||
▒██████▒▒ ▒██▒ ░ ░██▓ ▒██▒▒▒█████▓ ░▒████▓ ░▒████▒░██████▒
|
||||
▒ ▒▓▒ ▒ ░ ▒ ░░ ░ ▒▓ ░▒▓░░▒▓▒ ▒ ▒ ▒▒▓ ▒ ░░ ▒░ ░░ ▒░▓ ░
|
||||
░ ░▒ ░ ░ ░ ░▒ ░ ▒░░░▒░ ░ ░ ░ ▒ ▒ ░ ░ ░░ ░ ▒ ░
|
||||
░ ░ ░ ░ ░░ ░ ░░░ ░ ░ ░ ░ ░ ░ ░ ░
|
||||
░ ░ ░ ░ ░ ░ ░ ░
|
||||
░ `,
|
||||
`
|
||||
|
||||
███████╗████████╗██████╗ ██╗ ██╗██████╗ ███████╗██╗
|
||||
██╔════╝╚══██╔══╝██╔══██╗██║ ██║██╔══██╗██╔════╝██║
|
||||
███████╗ ██║ ██████╔╝██║ ██║██║ ██║█████╗ ██║
|
||||
╚════██║ ██║ ██╔══██╗██║ ██║██║ ██║██╔══╝ ██║
|
||||
███████║ ██║ ██║ ██║╚██████╔╝██████╔╝███████╗███████╗
|
||||
╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚══════╝
|
||||
`,
|
||||
];
|
||||
|
||||
export const randomHeadline = () => headlines[Math.floor(Math.random() * headlines.length)];
|
||||
Generated
+677
@@ -0,0 +1,677 @@
|
||||
{
|
||||
"name": "@strudel.cycles/server",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@strudel.cycles/server",
|
||||
"version": "0.0.1",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"speaker": "^0.5.4",
|
||||
"web-audio-api": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/aac": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/aac/-/aac-0.1.3.tgz",
|
||||
"integrity": "sha512-mhkqrzCOMndmoQovW43ry8tkMttuvKcpU0u9/K7zI+lF5NtCY2NE+HwAPiEgd3rHK7IuBT1ZsGL+LKeCPW9btQ==",
|
||||
"peerDependencies": {
|
||||
"av": "~0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/abc": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/abc/-/abc-0.5.1.tgz",
|
||||
"integrity": "sha512-WRBP9wjuEdmzw1yFHCVf7Qp/PXSmfuUSX3FSs+5+uLG4qGKmhOPKl8LtZaG0p6xxuB6hX1tIzH4VaHL23+bU5A==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/alac": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/alac/-/alac-0.1.0.tgz",
|
||||
"integrity": "sha512-+NBR/Nj/5zndHO7Oz4EOE7Qj+v+hk/0edJerBhjMQRznyjfaYV6/W+tUOOlLTZ0BWWD4p6eb11KKDHoPW5LsCw==",
|
||||
"peerDependencies": {
|
||||
"av": "~0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/assertion-error": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz",
|
||||
"integrity": "sha512-g/gZV+G476cnmtYI+Ko9d5khxSoCSoom/EaNmmCfwpOvBXEJ18qwFrxfP1/CsIqk2no1sAKKwxndV0tP7ROOFQ==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
|
||||
"integrity": "sha512-l6ToIJIotphWahxxHyzK9bnLR6kM4jJIIgLShZeqLY7iboHoGkdgFl7W2/Ivi4SkMJYGKqW8vSuk0uKUj6qsSw=="
|
||||
},
|
||||
"node_modules/audiobuffer": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/audiobuffer/-/audiobuffer-0.2.0.tgz",
|
||||
"integrity": "sha512-wEZ3iAOK1yNCy1PNcSe2V1pzzCV5rezYg0r/TRwojZBIVupQF56ioI0x5PGgatmWxJ4t8sWXymg8AFfopPXfrg==",
|
||||
"dependencies": {
|
||||
"underscore": "1.4.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/audiobuffer/node_modules/underscore": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz",
|
||||
"integrity": "sha512-ZqGrAgaqqZM7LGRzNjLnw5elevWb5M8LEoDMadxIW3OWbcv72wMMgKdwOKpd5Fqxe8choLD8HN3iSj3TUh/giQ=="
|
||||
},
|
||||
"node_modules/av": {
|
||||
"version": "0.4.9",
|
||||
"resolved": "https://registry.npmjs.org/av/-/av-0.4.9.tgz",
|
||||
"integrity": "sha512-MvkT0k+co6o+zLMrBFFeVhYcG/S/jzy+2p00c/VwA71q6g90J28qUNh93NabKrcN06bkwFK0OeiEpFsQd7TS7g==",
|
||||
"dependencies": {
|
||||
"coffeeify": "^0.6.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"speaker": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/av/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/av/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/av/node_modules/speaker": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/speaker/-/speaker-0.3.1.tgz",
|
||||
"integrity": "sha512-LEqSy+FHYHPZj4kX8NHylzaOmy+VIqj57enm2GS4B568hj0SdKDYa9jALCGQy43LZa/JmQk1iF0nlMVbS1PfPg==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"bindings": "^1.2.1",
|
||||
"debug": "^2.2.0",
|
||||
"nan": "^2.2.0",
|
||||
"readable-stream": "^2.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/bindings": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
||||
"dependencies": {
|
||||
"file-uri-to-path": "1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-alloc": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
|
||||
"integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
|
||||
"dependencies": {
|
||||
"buffer-alloc-unsafe": "^1.1.0",
|
||||
"buffer-fill": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-alloc-unsafe": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
|
||||
"integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="
|
||||
},
|
||||
"node_modules/buffer-fill": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
|
||||
"integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ=="
|
||||
},
|
||||
"node_modules/chai": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-1.7.2.tgz",
|
||||
"integrity": "sha512-iTItmoMR+S+g8g0xU7db2mrr2LeLMJ6Y+YJwJEOUSaVTzm6qyTBfj5r+5x+XQhlXUfVn6WfFS4sXpEtMg6Qwaw==",
|
||||
"dependencies": {
|
||||
"assertion-error": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/coffee-script": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz",
|
||||
"integrity": "sha512-W3s+SROY73OmrSGtPTTW/2wp2rmW5vuh0/tUuCK1NvTuyzLOVPccIP9whmhZ4cYWcr2NJPNENZIFaAMkTD5G3w==",
|
||||
"deprecated": "CoffeeScript on NPM has moved to \"coffeescript\" (no hyphen)",
|
||||
"dependencies": {
|
||||
"mkdirp": "~0.3.5"
|
||||
},
|
||||
"bin": {
|
||||
"cake": "bin/cake",
|
||||
"coffee": "bin/coffee"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/coffeeify": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/coffeeify/-/coffeeify-0.6.0.tgz",
|
||||
"integrity": "sha512-nt2rZwyeqLnzdbC15ZUBnQtokHLqNnbgfpUMYzAk1Pa33hObdtXnDV0SmTj2DWbzp47MpR87ix3uKHyutnzjsg==",
|
||||
"dependencies": {
|
||||
"coffee-script": "~1.7.0",
|
||||
"convert-source-map": "~0.3.3",
|
||||
"through": "~2.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/convert-source-map": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz",
|
||||
"integrity": "sha512-+4nRk0k3oEpwUB7/CalD7xE2z4VmtEnnq0GO2IPTkrooTrAhEsWvuLF5iWP1dXrwluki/azwXV1ve7gtYuPldg=="
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
|
||||
},
|
||||
"node_modules/flac": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/flac/-/flac-0.3.0.tgz",
|
||||
"integrity": "sha512-52oFJru3F/aELcnDwHtqDNXxruIZH7bE3vK6xQF4ALCelCZBXFggqPsPXPilVs9L242Iz1ROrbVX0UYXJMMinw==",
|
||||
"dependencies": {
|
||||
"abc": "0.5.x",
|
||||
"fsa": "0.5.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/fsa": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/fsa/-/fsa-0.5.1.tgz",
|
||||
"integrity": "sha512-HMdf0zJNXwP5dxROQb/ncUv+9BxuO4NM5R6/pSbUdfu5CUzMOg7+iP4tQUtBIv18PaScukzfxGIqLyJ3d9wg3Q==",
|
||||
"engines": {
|
||||
"node": ">=0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz",
|
||||
"integrity": "sha512-8OCq0De/h9ZxseqzCH8Kw/Filf5pF/vMI6+BH7Lu0jXz2pqYCjTAQRolSxRIi+Ax+oCCjlxoJMP0YQ4XlrQNHg==",
|
||||
"deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)"
|
||||
},
|
||||
"node_modules/mp3": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mp3/-/mp3-0.1.0.tgz",
|
||||
"integrity": "sha512-W9SQWAS8Gn7VvBHBSYnCIz2KAEzZccY+OAKJvO9JRRFBc0MfQ0icb7K4O9Sst/3SW1wcScC+zLNPYoiU6sSXww==",
|
||||
"peerDependencies": {
|
||||
"av": "~0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/nan": {
|
||||
"version": "2.16.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz",
|
||||
"integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/pcm-boilerplate": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/pcm-boilerplate/-/pcm-boilerplate-0.1.1.tgz",
|
||||
"integrity": "sha512-2mU5wENQGQoiuYUiRbLJtI8cH7H+1yvjR400XfGH6JONyzERbSbAIFCJnfRUSfbGJi2WJdo1gbTjiTsDyFSY4Q==",
|
||||
"dependencies": {
|
||||
"async": "0.2.x",
|
||||
"chai": "1.7.x",
|
||||
"underscore": "1.4.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/pcm-boilerplate/node_modules/async": {
|
||||
"version": "0.2.10",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
|
||||
"integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ=="
|
||||
},
|
||||
"node_modules/pcm-boilerplate/node_modules/underscore": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz",
|
||||
"integrity": "sha512-ZqGrAgaqqZM7LGRzNjLnw5elevWb5M8LEoDMadxIW3OWbcv72wMMgKdwOKpd5Fqxe8choLD8HN3iSj3TUh/giQ=="
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "2.3.7",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/speaker": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/speaker/-/speaker-0.5.4.tgz",
|
||||
"integrity": "sha512-0I35CJGgqU1rd/a3qVysR5gLlG+8QlzJcPAEnYvT0BLfuLdJ7JNdlQHwbh7ETNcXDXbzm2O148GEAoAER54Dvw==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"bindings": "^1.3.0",
|
||||
"buffer-alloc": "^1.1.0",
|
||||
"debug": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/through": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
|
||||
},
|
||||
"node_modules/underscore": {
|
||||
"version": "1.8.3",
|
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
|
||||
"integrity": "sha512-5WsVTFcH1ut/kkhAaHf4PVgI8c7++GiVcpCGxPouI6ZVjsqPnSDf8h/8HtVqc0t4fzRXwnMK70EcZeAs3PIddg=="
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/web-audio-api": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/web-audio-api/-/web-audio-api-0.2.2.tgz",
|
||||
"integrity": "sha512-XidLn3tEz8V9jwuV4OOVTOgNHp3WT6SQwv28Ad3lNXU4R9aPxWDYnocfVBh/OIna96jYPpiuP74KvSrTIF0OTg==",
|
||||
"dependencies": {
|
||||
"aac": "0.1.x",
|
||||
"alac": "0.1.x",
|
||||
"async": "0.9.x",
|
||||
"audiobuffer": "0.2.x",
|
||||
"av": "0.4.x",
|
||||
"flac": "0.3.x",
|
||||
"mp3": "0.1.x",
|
||||
"pcm-boilerplate": "0.1.x",
|
||||
"underscore": "1.8.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"aac": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/aac/-/aac-0.1.3.tgz",
|
||||
"integrity": "sha512-mhkqrzCOMndmoQovW43ry8tkMttuvKcpU0u9/K7zI+lF5NtCY2NE+HwAPiEgd3rHK7IuBT1ZsGL+LKeCPW9btQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"abc": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/abc/-/abc-0.5.1.tgz",
|
||||
"integrity": "sha512-WRBP9wjuEdmzw1yFHCVf7Qp/PXSmfuUSX3FSs+5+uLG4qGKmhOPKl8LtZaG0p6xxuB6hX1tIzH4VaHL23+bU5A=="
|
||||
},
|
||||
"alac": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/alac/-/alac-0.1.0.tgz",
|
||||
"integrity": "sha512-+NBR/Nj/5zndHO7Oz4EOE7Qj+v+hk/0edJerBhjMQRznyjfaYV6/W+tUOOlLTZ0BWWD4p6eb11KKDHoPW5LsCw==",
|
||||
"requires": {}
|
||||
},
|
||||
"assertion-error": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz",
|
||||
"integrity": "sha512-g/gZV+G476cnmtYI+Ko9d5khxSoCSoom/EaNmmCfwpOvBXEJ18qwFrxfP1/CsIqk2no1sAKKwxndV0tP7ROOFQ=="
|
||||
},
|
||||
"async": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
|
||||
"integrity": "sha512-l6ToIJIotphWahxxHyzK9bnLR6kM4jJIIgLShZeqLY7iboHoGkdgFl7W2/Ivi4SkMJYGKqW8vSuk0uKUj6qsSw=="
|
||||
},
|
||||
"audiobuffer": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/audiobuffer/-/audiobuffer-0.2.0.tgz",
|
||||
"integrity": "sha512-wEZ3iAOK1yNCy1PNcSe2V1pzzCV5rezYg0r/TRwojZBIVupQF56ioI0x5PGgatmWxJ4t8sWXymg8AFfopPXfrg==",
|
||||
"requires": {
|
||||
"underscore": "1.4.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"underscore": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz",
|
||||
"integrity": "sha512-ZqGrAgaqqZM7LGRzNjLnw5elevWb5M8LEoDMadxIW3OWbcv72wMMgKdwOKpd5Fqxe8choLD8HN3iSj3TUh/giQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"av": {
|
||||
"version": "0.4.9",
|
||||
"resolved": "https://registry.npmjs.org/av/-/av-0.4.9.tgz",
|
||||
"integrity": "sha512-MvkT0k+co6o+zLMrBFFeVhYcG/S/jzy+2p00c/VwA71q6g90J28qUNh93NabKrcN06bkwFK0OeiEpFsQd7TS7g==",
|
||||
"requires": {
|
||||
"coffeeify": "^0.6.0",
|
||||
"speaker": "^0.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"optional": 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==",
|
||||
"optional": true
|
||||
},
|
||||
"speaker": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/speaker/-/speaker-0.3.1.tgz",
|
||||
"integrity": "sha512-LEqSy+FHYHPZj4kX8NHylzaOmy+VIqj57enm2GS4B568hj0SdKDYa9jALCGQy43LZa/JmQk1iF0nlMVbS1PfPg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"bindings": "^1.2.1",
|
||||
"debug": "^2.2.0",
|
||||
"nan": "^2.2.0",
|
||||
"readable-stream": "^2.0.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"bindings": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
||||
"requires": {
|
||||
"file-uri-to-path": "1.0.0"
|
||||
}
|
||||
},
|
||||
"buffer-alloc": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
|
||||
"integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
|
||||
"requires": {
|
||||
"buffer-alloc-unsafe": "^1.1.0",
|
||||
"buffer-fill": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"buffer-alloc-unsafe": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
|
||||
"integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="
|
||||
},
|
||||
"buffer-fill": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
|
||||
"integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ=="
|
||||
},
|
||||
"chai": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-1.7.2.tgz",
|
||||
"integrity": "sha512-iTItmoMR+S+g8g0xU7db2mrr2LeLMJ6Y+YJwJEOUSaVTzm6qyTBfj5r+5x+XQhlXUfVn6WfFS4sXpEtMg6Qwaw==",
|
||||
"requires": {
|
||||
"assertion-error": "1.0.0"
|
||||
}
|
||||
},
|
||||
"coffee-script": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz",
|
||||
"integrity": "sha512-W3s+SROY73OmrSGtPTTW/2wp2rmW5vuh0/tUuCK1NvTuyzLOVPccIP9whmhZ4cYWcr2NJPNENZIFaAMkTD5G3w==",
|
||||
"requires": {
|
||||
"mkdirp": "~0.3.5"
|
||||
}
|
||||
},
|
||||
"coffeeify": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/coffeeify/-/coffeeify-0.6.0.tgz",
|
||||
"integrity": "sha512-nt2rZwyeqLnzdbC15ZUBnQtokHLqNnbgfpUMYzAk1Pa33hObdtXnDV0SmTj2DWbzp47MpR87ix3uKHyutnzjsg==",
|
||||
"requires": {
|
||||
"coffee-script": "~1.7.0",
|
||||
"convert-source-map": "~0.3.3",
|
||||
"through": "~2.3.4"
|
||||
}
|
||||
},
|
||||
"convert-source-map": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz",
|
||||
"integrity": "sha512-+4nRk0k3oEpwUB7/CalD7xE2z4VmtEnnq0GO2IPTkrooTrAhEsWvuLF5iWP1dXrwluki/azwXV1ve7gtYuPldg=="
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"optional": true
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
|
||||
},
|
||||
"flac": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/flac/-/flac-0.3.0.tgz",
|
||||
"integrity": "sha512-52oFJru3F/aELcnDwHtqDNXxruIZH7bE3vK6xQF4ALCelCZBXFggqPsPXPilVs9L242Iz1ROrbVX0UYXJMMinw==",
|
||||
"requires": {
|
||||
"abc": "0.5.x",
|
||||
"fsa": "0.5.x"
|
||||
}
|
||||
},
|
||||
"fsa": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/fsa/-/fsa-0.5.1.tgz",
|
||||
"integrity": "sha512-HMdf0zJNXwP5dxROQb/ncUv+9BxuO4NM5R6/pSbUdfu5CUzMOg7+iP4tQUtBIv18PaScukzfxGIqLyJ3d9wg3Q=="
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"optional": true
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"optional": true
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz",
|
||||
"integrity": "sha512-8OCq0De/h9ZxseqzCH8Kw/Filf5pF/vMI6+BH7Lu0jXz2pqYCjTAQRolSxRIi+Ax+oCCjlxoJMP0YQ4XlrQNHg=="
|
||||
},
|
||||
"mp3": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mp3/-/mp3-0.1.0.tgz",
|
||||
"integrity": "sha512-W9SQWAS8Gn7VvBHBSYnCIz2KAEzZccY+OAKJvO9JRRFBc0MfQ0icb7K4O9Sst/3SW1wcScC+zLNPYoiU6sSXww==",
|
||||
"requires": {}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.16.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz",
|
||||
"integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==",
|
||||
"optional": true
|
||||
},
|
||||
"pcm-boilerplate": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/pcm-boilerplate/-/pcm-boilerplate-0.1.1.tgz",
|
||||
"integrity": "sha512-2mU5wENQGQoiuYUiRbLJtI8cH7H+1yvjR400XfGH6JONyzERbSbAIFCJnfRUSfbGJi2WJdo1gbTjiTsDyFSY4Q==",
|
||||
"requires": {
|
||||
"async": "0.2.x",
|
||||
"chai": "1.7.x",
|
||||
"underscore": "1.4.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": {
|
||||
"version": "0.2.10",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
|
||||
"integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ=="
|
||||
},
|
||||
"underscore": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz",
|
||||
"integrity": "sha512-ZqGrAgaqqZM7LGRzNjLnw5elevWb5M8LEoDMadxIW3OWbcv72wMMgKdwOKpd5Fqxe8choLD8HN3iSj3TUh/giQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"optional": true
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.7",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.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==",
|
||||
"optional": true
|
||||
},
|
||||
"speaker": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/speaker/-/speaker-0.5.4.tgz",
|
||||
"integrity": "sha512-0I35CJGgqU1rd/a3qVysR5gLlG+8QlzJcPAEnYvT0BLfuLdJ7JNdlQHwbh7ETNcXDXbzm2O148GEAoAER54Dvw==",
|
||||
"requires": {
|
||||
"bindings": "^1.3.0",
|
||||
"buffer-alloc": "^1.1.0",
|
||||
"debug": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"through": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
|
||||
},
|
||||
"underscore": {
|
||||
"version": "1.8.3",
|
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
|
||||
"integrity": "sha512-5WsVTFcH1ut/kkhAaHf4PVgI8c7++GiVcpCGxPouI6ZVjsqPnSDf8h/8HtVqc0t4fzRXwnMK70EcZeAs3PIddg=="
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"optional": true
|
||||
},
|
||||
"web-audio-api": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/web-audio-api/-/web-audio-api-0.2.2.tgz",
|
||||
"integrity": "sha512-XidLn3tEz8V9jwuV4OOVTOgNHp3WT6SQwv28Ad3lNXU4R9aPxWDYnocfVBh/OIna96jYPpiuP74KvSrTIF0OTg==",
|
||||
"requires": {
|
||||
"aac": "0.1.x",
|
||||
"alac": "0.1.x",
|
||||
"async": "0.9.x",
|
||||
"audiobuffer": "0.2.x",
|
||||
"av": "0.4.x",
|
||||
"flac": "0.3.x",
|
||||
"mp3": "0.1.x",
|
||||
"pcm-boilerplate": "0.1.x",
|
||||
"underscore": "1.8.x"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@strudel.cycles/server",
|
||||
"version": "0.0.1",
|
||||
"description": "Experimental Strudel node server",
|
||||
"main": "server.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "node server.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"speaker": "^0.5.4",
|
||||
"web-audio-api": "^0.2.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import repl from 'repl';
|
||||
import { evalScope, evaluate } from '@strudel.cycles/eval';
|
||||
import { AudioContext } from 'web-audio-api';
|
||||
import Speaker from 'speaker';
|
||||
import controls from '@strudel.cycles/core/controls.mjs';
|
||||
import { prepareSamples, playBuffer } from './sampler.js';
|
||||
import { randomHeadline } from './headline.js';
|
||||
|
||||
/*
|
||||
This is just a POC of how a strudel cli / repl could look like
|
||||
*/
|
||||
|
||||
// setup audio
|
||||
const context = new AudioContext();
|
||||
context.outStream = new Speaker({
|
||||
channels: context.format.numberOfChannels,
|
||||
bitDepth: context.format.bitDepth,
|
||||
sampleRate: context.sampleRate,
|
||||
});
|
||||
console.log('now loading samples..');
|
||||
const buffers = await prepareSamples(context, './samples/');
|
||||
console.log('');
|
||||
|
||||
// scheduler
|
||||
let tick = 0;
|
||||
const slice = 0.5;
|
||||
const latency = 1;
|
||||
/* console.log('latency', latency);
|
||||
console.log('querying every', slice, 's'); */
|
||||
const offset = context.currentTime + latency;
|
||||
let pattern;
|
||||
setInterval(() => {
|
||||
if (pattern) {
|
||||
try {
|
||||
pattern
|
||||
.queryArc(tick * slice, ++tick * slice)
|
||||
.filter((hap) => hap.hasOnset())
|
||||
.forEach((hap) => {
|
||||
const t = hap.whole.begin.valueOf() + offset;
|
||||
const { s, n } = hap.value;
|
||||
const bank = buffers[s];
|
||||
if (bank?.length) {
|
||||
const index = (n ?? 0) % bank.length;
|
||||
playBuffer(context, bank[index], t);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('query error', err);
|
||||
}
|
||||
}
|
||||
}, slice * 1000);
|
||||
|
||||
await evalScope(controls, import('@strudel.cycles/core'), import('@strudel.cycles/mini'));
|
||||
console.log(`${randomHeadline()}
|
||||
|
||||
welcome to the experimental strudel cli <3
|
||||
|
||||
available samples: ${Object.entries(buffers)
|
||||
.map(([key, samples]) => `${key} (${samples.length})`)
|
||||
.join(', ')}
|
||||
you can add samples to the samples folder (.wav, .ogg, .mp3)
|
||||
|
||||
please enter a pattern, e.g. s("bd(3,8),~ sd,hh(3,8,1)")
|
||||
`);
|
||||
// repl
|
||||
repl.start({
|
||||
prompt: '>>>',
|
||||
eval: async function myEval(cmd, context, filename, callback) {
|
||||
try {
|
||||
pattern = (await evaluate(cmd)).pattern;
|
||||
} catch (err) {
|
||||
console.error('query error', err.message);
|
||||
}
|
||||
callback(null);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
import fs from 'fs';
|
||||
|
||||
export const loadSample = (context, path) => {
|
||||
console.log(`load sample: ${path}`);
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readFile(new URL(path, import.meta.url), function (err, buffer) {
|
||||
if (err) reject(err);
|
||||
context.decodeAudioData(buffer, resolve);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export async function prepareSamples(context, path, ignore = ['.DS_Store', 'Thumbs.db']) {
|
||||
const ignored = (filename) => !ignore.includes(filename);
|
||||
const folders = fs.readdirSync(path).filter(ignored);
|
||||
return Object.fromEntries(
|
||||
await Promise.all(
|
||||
folders.map(async (folder) => {
|
||||
return [
|
||||
folder,
|
||||
await Promise.all(
|
||||
fs
|
||||
.readdirSync(path + folder)
|
||||
.filter(ignored)
|
||||
.map((filename) => path + folder + '/' + filename)
|
||||
.map((filepath) => loadSample(context, filepath)),
|
||||
),
|
||||
];
|
||||
}, []),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export const playBuffer = (context, audioBuffer, start = 0) => {
|
||||
const bufferNode = context.createBufferSource();
|
||||
bufferNode.connect(context.destination);
|
||||
bufferNode.buffer = audioBuffer;
|
||||
bufferNode.loop = false;
|
||||
bufferNode.start(start);
|
||||
bufferNode.stop(start + audioBuffer.length / audioBuffer.sampleRate);
|
||||
};
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user