Compare commits

..

46 Commits

Author SHA1 Message Date
Felix Roos 5cf9f9fef4 built in webaudio prebake ? 2022-07-01 10:45:43 +02:00
Felix Roos e44313f631 basic static sample server 2022-07-01 10:45:25 +02:00
Felix Roos 8a1cd32d50 Merge pull request #148 from tidalcycles/webaudio-optimizations
Sampler optimizations and more
2022-06-28 21:54:34 +02:00
Felix Roos 2a18b27114 rerun snapshots 2022-06-28 21:51:11 +02:00
Felix Roos 55f91143d9 rename choke -> clip 2022-06-28 21:50:08 +02:00
Felix Roos cf564a7d06 fix tests 2022-06-27 23:52:02 +02:00
Felix Roos b55067ad60 load piano samples locally 2022-06-27 23:32:45 +02:00
Felix Roos a437c698eb style version log + check if already loaded 2022-06-27 23:17:46 +02:00
Felix Roos f8ada70fe7 add tune 2022-06-27 22:40:59 +02:00
Felix Roos 196d9db3fa log core version + add warning to isPattern 2022-06-27 22:40:50 +02:00
Felix Roos e29eaf3785 fix splitSN 2022-06-27 00:57:27 +02:00
Felix Roos 7048ea473f migrate tone piano to own piano 2022-06-27 00:50:24 +02:00
Felix Roos 720b1eeb27 fix soundfonts + samples without base 2022-06-27 00:49:58 +02:00
Felix Roos 4e282dac5b migrate underground plumber 2022-06-27 00:26:29 +02:00
Felix Roos 42343254b1 fix single sampels 2022-06-27 00:26:21 +02:00
Felix Roos e98829c96b tame oscillator gain 2022-06-27 00:26:11 +02:00
Felix Roos 5791f12a28 add prebake script with piano 2022-06-27 00:04:33 +02:00
Felix Roos fe9a6e8fdd draw note property if set 2022-06-27 00:03:26 +02:00
Felix Roos dd3f4e4751 support "s" as "#" alternative inside note names 2022-06-27 00:03:06 +02:00
Felix Roos a89fa766ea support colon sign as normal char 2022-06-27 00:02:33 +02:00
Felix Roos 39c2cf6c72 add build:parser script once more.. 2022-06-27 00:01:59 +02:00
Felix Roos c1ce72469c webaudio optimizations
- samples with obejct format for pitch declaration
- support note to repitch samples
- support choke to fit samples to hap duration
- support "legacy" context.velocity in .out
- support ":" inside s or note to set n
- fix sample fadeout for soundfonts and choke
- move gain before filters
2022-06-27 00:01:28 +02:00
Felix Roos eca76dd7c4 refactor share for build without top level await 2022-06-26 13:05:36 +02:00
Felix Roos 621b2d8fc9 Merge pull request #146 from tidalcycles/share-link
can now generate short link for sharing
2022-06-26 12:58:53 +02:00
Felix Roos 4039b962b4 can now generate short link for sharing 2022-06-26 12:49:29 +02:00
Felix Roos 3d314324e0 Merge pull request #144 from tidalcycles/flash-effect
flash effect on ctrl enter
2022-06-24 22:16:03 +02:00
Felix Roos b9829bb696 flash effect on ctrl enter 2022-06-24 21:13:17 +02:00
Felix Roos 85f03fb1d0 Merge pull request #142 from tidalcycles/pianoroll-object-support
Pianoroll Object Support
2022-06-22 20:18:17 +02:00
Felix Roos 69810303e5 remove old code 2022-06-22 20:17:12 +02:00
Felix Roos 5b0e31acf3 add tune 2022-06-22 20:13:41 +02:00
alex f0a934e014 Publish
- @strudel.cycles/serial@0.1.3
 - @strudel.cycles/soundfonts@0.1.1
 - @strudel.cycles/webaudio@0.1.4
2022-06-22 13:35:13 +01:00
alex 328e4db1a2 bump 2022-06-21 22:00:08 +01:00
Alex McLean a708265e14 Merge pull request #141 from tidalcycles/serial-twiddles
Serial twiddles
2022-06-21 21:57:27 +01:00
alex a0618f7f47 remove console logging 2022-06-21 21:47:29 +01:00
alex f2bd9fae01 remove hackery 2022-06-21 21:46:23 +01:00
Felix Roos 64f717f81b object support for pianoroll 2022-06-21 22:38:05 +02:00
Felix Roos fd17377c18 Merge pull request #139 from tidalcycles/soundfonts
Soundfont Support
2022-06-21 22:20:04 +02:00
alex 6698fdb6e6 Merge branch 'main' into serial-twiddles 2022-06-21 17:03:02 +01:00
alex aee9bf7cc2 remove async from serial, default baudrate to 38400 2022-06-21 15:54:18 +01:00
Felix Roos 319400ab13 remove unused sf control 2022-06-20 22:20:22 +02:00
Felix Roos 38b1717fac load fonts from forked version 2022-06-20 22:00:14 +02:00
Felix Roos d5938a016c fix looping fonts 2022-06-20 20:55:20 +02:00
Felix Roos 82d6103907 began soundfont implementation 2022-06-20 20:02:55 +02:00
Felix Roos b353db67d4 began soundfonts package 2022-06-19 15:31:19 +02:00
alex 23be3e5ba3 hackery 2022-06-17 14:45:58 +01:00
alex 34ba762b42 funky serialisation for serial 2022-06-16 13:54:53 +01:00
68 changed files with 5806 additions and 3998 deletions
+275 -49
View File
@@ -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"
+1
View File
@@ -10,6 +10,7 @@
"snapshot": "cd repl && npm run snapshot",
"repl": "cd repl && npm run dev",
"osc": "cd packages/osc && npm run server",
"sampler": "cd ./repl/public/samples && node ../../packages/webaudio/sample-server.js",
"build": "rm -rf out && cd repl && npm run build && cd ../tutorial && npm run build",
"preview": "npx serve ./out",
"deploy": "gh-pages -d out",
+1
View File
@@ -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
+15 -1
View File
@@ -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;
+11 -2
View File
@@ -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));
+2 -2
View File
@@ -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) => {
+164 -158
View File
@@ -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
};
+1 -1
View File
@@ -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]]
+1031
View File
File diff suppressed because it is too large Load Diff
+5 -1
View File
@@ -5,7 +5,8 @@
"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",
@@ -28,5 +29,8 @@
"@strudel.cycles/core": "^0.1.2",
"@strudel.cycles/eval": "^0.1.3",
"@strudel.cycles/tone": "^0.1.3"
},
"devDependencies": {
"peggy": "^2.0.1"
}
}
+2 -2
View File
File diff suppressed because one or more lines are too long
+35 -10
View File
@@ -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 };
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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": {
+23 -5
View File
@@ -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 {
+13
View File
@@ -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));
+114
View File
@@ -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));
}
}
}
+6
View File
@@ -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
+301
View File
@@ -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=="
}
}
}
+31
View File
@@ -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"
}
}
+1 -2
View File
@@ -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);
+9 -3
View File
@@ -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);
+1
View File
@@ -8,3 +8,4 @@ export * from './clockworker.mjs';
export * from './scheduler.mjs';
export * from './webaudio.mjs';
export * from './sampler.mjs';
export * from './prebake.mjs';
+835 -3637
View File
File diff suppressed because it is too large Load Diff
+6 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/webaudio",
"version": "0.1.3",
"version": "0.1.4",
"description": "Web Audio helpers for Strudel",
"main": "index.mjs",
"type": "module",
@@ -29,5 +29,10 @@
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.1.2"
},
"devDependencies": {
"koa": "^2.13.4",
"koa-static": "^5.0.0",
"@koa/cors": "^3.3.0"
}
}
+83
View File
@@ -0,0 +1,83 @@
import { Pattern, toMidi } from '@strudel.cycles/core';
import { samples } from '.';
let baking;
export function prebake(props) {
if (baking) {
return baking;
}
baking = prebaker(props);
return baking;
}
async function prebaker(props = {}) {
const { baseURL = './samples' } = props; // http://localhost:1234
try {
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
baseURL + '/piano/',
);
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 };
});
};
// ping sample server
/* await fetch(samplerURL)
.then(() => {
console.log(`prebake: Using samples from "${baseURL}"`);
})
.catch(() => {
throw new Error(
`sample server not running at "${samplerURL}"... run "npm run sampler" from the strudel project root.`,
);
}); */
} catch (e) {
console.warn('prebake error', e);
}
}
+34
View File
@@ -0,0 +1,34 @@
// "sampler": "cd ./repl/public/samples && node ../../../packages/webaudio/sample-server.js",
// "sampler": "http-server ./repl/public",
import fs from 'fs';
import path from 'path';
import Koa from 'koa'; // CJS: require('koa');
import cors from '@koa/cors';
import serve from 'koa-static'; // CJS: require('koa-static')
const cwd = process.cwd();
const yellow = '\x1b[33m%s\x1b[0m';
// get array of all folders in cwd:
const banks = fs.readdirSync(cwd).filter((f) => fs.statSync(path.join(cwd, f)).isDirectory());
const app = new Koa();
app.use(cors());
app.use(serve(cwd));
app.use((ctx, next) => {
return next().then(() => {
if (ctx.path === '/') {
ctx.body = 'banks: ' + banks.join(', ');
}
});
});
const port = 1234;
app.listen(port);
console.log(yellow, `strudel sampler running at http://localhost:${port}`);
// node: get current working directory
console.log(yellow, 'cwd: ' + cwd);
console.log('found banks:');
banks.map((c) => console.log(yellow, c));
+23 -7
View File
@@ -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
View File
@@ -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]);
+506 -10
View File
@@ -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",
+2
View File
@@ -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
View File
@@ -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">
+57
View File
@@ -0,0 +1,57 @@
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/',
);
}
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 };
});
};
+3
View File
@@ -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
View File
@@ -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})`;
File diff suppressed because one or more lines are too long