Compare commits

..

160 Commits

Author SHA1 Message Date
Felix Roos 14184993d0 Merge remote-tracking branch 'origin/main' into fix-scale-offset 2023-02-09 19:06:42 +01:00
Felix Roos c350f262e6 Merge pull request #425 from tidalcycles/pwa
add cdn.freesound to cache list
2023-02-09 08:56:42 +01:00
Felix Roos 2a2c7437e4 add cdn.freesound to cache list 2023-02-08 21:18:03 +01:00
Felix Roos 5656cd2fc6 Merge pull request #421 from tidalcycles/pwa
add more offline caching
2023-02-08 20:36:00 +01:00
Felix Roos b6e42876ad fix cache rule + disable service worker in dev 2023-02-08 20:32:03 +01:00
Felix Roos 3671344867 add freesound + shabda to offline cache rule 2023-02-08 20:26:44 +01:00
Felix Roos 9d661808ba Merge pull request #419 from tidalcycles/pwa
add caching strategy for missing file types + cache all samples loaded from github
2023-02-07 22:08:01 +01:00
Felix Roos 6a5d849b3c add caching strategy for missing file types
+ cache all samples loaded from github
2023-02-07 21:50:08 +01:00
Felix Roos 25bbd73a90 Merge pull request #417 from tidalcycles/pwa
PWA with offline support
2023-02-06 23:29:56 +01:00
Felix Roos 3d27a5f8c7 format 2023-02-06 21:06:33 +01:00
Felix Roos 44ef372399 update og meta data 2023-02-06 21:05:28 +01:00
Felix Roos fd09dff381 basic pwa setup 2023-02-06 20:57:34 +01:00
Felix Roos 10580a0bec change rocket to strudel 2023-02-06 20:21:09 +01:00
Felix Roos f3f87b0a46 dont lint json 2023-02-06 20:20:45 +01:00
Felix Roos ac606bcf71 Update README.md 2023-02-05 23:16:38 +01:00
alex bb50df07e7 iclc reviews 2023-02-05 21:01:02 +00:00
Felix Roos f7ac033e1c Merge pull request #411 from tidalcycles/docs
improve samples doc
2023-02-05 17:27:32 +01:00
Felix Roos a1043352c1 Merge pull request #413 from tidalcycles/remove-google
google gtfo
2023-02-05 17:27:09 +01:00
Felix Roos e15fecf6cf google gtfo 2023-02-05 17:19:32 +01:00
Felix Roos 3e99f345ef better headings 2023-02-05 16:40:29 +01:00
Felix Roos 0a77770ef3 remove double directive 2023-02-05 16:37:00 +01:00
Felix Roos 379b8392dd add snapshot 2023-02-05 16:32:51 +01:00
Felix Roos 0fdae70461 improve samples doc 2023-02-05 16:28:43 +01:00
Felix Roos 087423c754 Merge pull request #409 from tidalcycles/docs
improve effects doc
2023-02-05 16:27:59 +01:00
Felix Roos ce64c98fb7 add adsr image link 2023-02-05 15:08:17 +01:00
Felix Roos e11a8a9dbb Merge pull request #410 from bwagner/patch-3
Update effects.mdx
2023-02-05 14:56:03 +01:00
Bernhard Wagner ef4a68a24d Update effects.mdx
Wether -> Whether
2023-02-05 14:54:27 +01:00
Felix Roos 0ec65c85d3 fix examples + snapshots 2023-02-05 14:52:01 +01:00
Felix Roos 74326f226d format 2023-02-05 14:42:08 +01:00
Felix Roos 554e0d7a39 remove envelope section from synths page 2023-02-05 14:38:47 +01:00
Felix Roos 1b99ac92dd categorize effects + add envelope doc
+ use short, more logical filter params as default
2023-02-05 14:34:59 +01:00
Felix Roos ff0b77908a Merge branch 'main' into docs 2023-02-05 14:34:16 +01:00
Felix Roos 83d19638ba Merge pull request #407 from tidalcycles/shabda-doc
add shabda doc
2023-02-03 19:54:47 +01:00
Felix Roos a7d484b1a8 add shabda doc 2023-02-03 19:53:01 +01:00
Felix Roos b92d1d09a2 tonleiter experiment 2023-02-03 19:43:10 +01:00
Felix Roos c79b2653e7 revert change for now
(because tonal degree does not support floats)
2023-02-03 19:42:35 +01:00
Felix Roos 554b72c916 fix scale degrees
fixes https://github.com/tidalcycles/strudel/issues/378
2023-02-02 22:11:56 +01:00
Felix Roos d87beb37b8 Merge pull request #405 from tidalcycles/fix-share-on-subpath
fix: share url on subpath
2023-02-02 21:45:56 +01:00
Felix Roos dcdb383960 fix: share url on subpath
fixes https://github.com/tidalcycles/strudel/issues/389
2023-02-02 21:43:24 +01:00
Felix Roos 478a7e95ea Merge pull request #404 from tidalcycles/csound-update
update csound + fix sound output
2023-02-02 21:35:45 +01:00
Felix Roos f7b3b582cc update csound + fix sound output
see https://github.com/csound/csound/issues/1690#issuecomment-1414301145
2023-02-02 21:31:44 +01:00
Felix Roos d1719dc277 Merge pull request #403 from tidalcycles/downgrade-csound
pin @csound/browser to 6.18.3 + bump
2023-02-02 19:53:36 +01:00
Felix Roos 9d8c812aa3 pin @csound/browser to 6.18.3 + bump 2023-02-02 19:51:16 +01:00
Felix Roos 2e390acce9 hotfix: deploy workflow install command 2023-02-01 23:04:13 +01:00
Felix Roos e04797d891 Merge pull request #400 from tidalcycles/awaitable-audio-init
release webaudio
2023-02-01 22:47:26 +01:00
Felix Roos fb6195b081 bump webaudio 2023-02-01 22:45:53 +01:00
Felix Roos c6491a3ccd Merge pull request #399 from tidalcycles/awaitable-audio-init
can now await initAudio + initAudioOnFirstClick
2023-02-01 22:45:16 +01:00
Felix Roos 014ae2107e can now await initAudio + initAudioOnFirstClick 2023-02-01 22:42:25 +01:00
Felix Roos 1457eb9d19 Merge pull request #398 from tidalcycles/fix-minirepl-styles
fix: minirepl styles
2023-02-01 22:32:18 +01:00
Felix Roos 6f5f8cc7d8 fix: minirepl styles 2023-02-01 22:30:24 +01:00
Felix Roos 9ec3fee455 Merge pull request #396 from tidalcycles/pnpm
proper builds + use pnpm workspaces
2023-02-01 22:17:18 +01:00
Felix Roos 2d52168c26 trash obsolete package-lock files 2023-02-01 20:43:16 +01:00
Felix Roos aa5dd7920f organize tasks 2023-02-01 20:38:56 +01:00
Felix Roos d60956e82a update contribution guide to pnpm
+ update root tasks
2023-02-01 20:33:13 +01:00
Alex McLean 91fd3fb5ca add missing snapshots 2023-02-01 15:51:42 +00:00
Alex McLean 46f3b662a7 add pattern methods hurry, press and pressBy (#397) 2023-02-01 15:49:55 +00:00
Felix Roos 54a61dcdae overhaul the remaining readmes 2023-02-01 16:29:18 +01:00
Felix Roos b58f5232c7 add nano-repl as workspace 2023-02-01 16:09:50 +01:00
Felix Roos 9a5bda862c update examples 2023-02-01 16:09:33 +01:00
Felix Roos 7f67adb639 bump eval 2023-02-01 15:49:05 +01:00
Felix Roos 48fd943622 fix: eval dependency + add deprecation note 2023-02-01 15:40:57 +01:00
Felix Roos 08350d00be remove sc prefix to fix build 2023-02-01 15:35:21 +01:00
Felix Roos 84bf4a2686 add pretest 2023-01-30 22:53:53 +01:00
Felix Roos 2823456e96 update lockfile 2023-01-30 22:50:44 +01:00
Felix Roos a373629fc2 fix: gh actions use pnpm 2023-01-30 22:48:24 +01:00
Felix Roos 0aa2f72a3a update core examples 2023-01-29 23:27:35 +01:00
Felix Roos c032f6dde1 add prepublishOnly everywhere 2023-01-29 00:14:07 +01:00
Felix Roos f448d8f334 rename website package 2023-01-29 00:08:29 +01:00
Felix Roos a5d4a43753 xen build 2023-01-28 23:19:33 +01:00
Felix Roos 52e01321ec webdirt build 2023-01-28 23:18:25 +01:00
Felix Roos 19692e0122 webaudio build 2023-01-28 23:14:56 +01:00
Felix Roos cd1da3cb81 transpiler build 2023-01-28 23:12:00 +01:00
Felix Roos ffaa8f4ab3 tone build 2023-01-28 23:09:25 +01:00
Felix Roos f3da9d680d bump 2023-01-28 23:07:55 +01:00
Felix Roos 6118bd24f2 tonal build 2023-01-28 23:06:59 +01:00
Felix Roos fddbb2e2ac soundfonts build 2023-01-28 23:04:30 +01:00
Felix Roos cb7156b091 serial build 2023-01-28 23:01:20 +01:00
Felix Roos b5914a3f12 react build 2023-01-28 22:58:40 +01:00
Felix Roos 348d576ff3 remove dist folder 2023-01-28 22:49:13 +01:00
Felix Roos 7507654749 osc build 2023-01-28 22:40:10 +01:00
Felix Roos c25aad0c4a midi build 2023-01-28 22:35:08 +01:00
Felix Roos 4aa32415cd eval build 2023-01-28 22:32:48 +01:00
Felix Roos cde23bbf98 csound build 2023-01-28 22:27:18 +01:00
Felix Roos 555fe0b629 bump mini 2023-01-28 22:16:33 +01:00
Felix Roos be44c28fd3 add mini build 2023-01-28 22:16:04 +01:00
Felix Roos 657091307e remove unused deps 2023-01-28 22:12:11 +01:00
Felix Roos 458dc6e07d add core build 2023-01-28 22:05:11 +01:00
Felix Roos cfaecf9fcb migrate lerna to pnpm
https://lerna.js.org/docs/recipes/using-pnpm-with-lerna
2023-01-28 22:01:35 +01:00
Felix Roos 902f722d4c add import linting + fix checks 2023-01-28 21:00:42 +01:00
Felix Roos ea2d8b2773 use workspace protocol everywhere 2023-01-28 20:37:56 +01:00
Felix Roos b319754dc3 fix website build 2023-01-28 20:31:23 +01:00
Felix Roos 35f2040717 use pnpm workspaces 2023-01-28 20:14:26 +01:00
Felix Roos 4121fc926e write more about packages / monorepo setup 2023-01-28 17:59:59 +01:00
Felix Roos fd9ab987e6 Merge branch 'main' into docs 2023-01-28 14:23:01 +01:00
Felix Roos 6c3a235b08 Merge pull request #379 from urswilke/add-tidal-drum-patterns-to-examples
Add tidal-drum-patterns to examples
2023-01-27 14:40:40 +01:00
Felix Roos 07cb861385 Merge pull request #388 from tidalcycles/ribbon
add ribbon + test + docs
2023-01-27 14:39:56 +01:00
Alex McLean 217e348fc3 Notes are not essential :) (#393)
* Notes are not essential :)

* Update notes.mdx
2023-01-27 11:10:37 +00:00
Felix Roos ad917afba4 Merge pull request #391 from tidalcycles/docs
document csound
2023-01-21 17:18:12 +01:00
Felix Roos 9539c6d317 fix: left sidebar scroll 2023-01-21 17:14:18 +01:00
Felix Roos e946e1e01e add csound docs 2023-01-21 17:05:57 +01:00
Felix Roos fea1e71a99 Merge remote-tracking branch 'origin/main' into docs 2023-01-21 15:57:54 +01:00
Felix Roos 9731eb8c5b add ribbon + test + docs 2023-01-19 12:25:12 +01:00
Felix Roos e058ae54ef Merge pull request #387 from tidalcycles/rename-a-to-angle
Rename a to angle
2023-01-19 12:04:50 +01:00
Felix Roos ba6714f384 add draw examples 2023-01-19 12:00:00 +01:00
Felix Roos 0a0a761e23 rename a to angle 2023-01-19 11:59:43 +01:00
Felix Roos 88e8368022 Merge pull request #386 from tidalcycles/run
add run + test + docs
2023-01-19 11:49:38 +01:00
Felix Roos 468ccc6288 add run + test + docs 2023-01-19 11:47:11 +01:00
Felix Roos 77e0237148 Merge pull request #385 from tidalcycles/docs-use-note-instead-of-n
docs: use note instead of n to mitigate confusion
2023-01-19 11:32:55 +01:00
Felix Roos ffdfb07f72 docs: use note instead of n to mitigate confusion 2023-01-19 11:27:54 +01:00
Felix Roos 203ed88024 Merge pull request #384 from tidalcycles/update-my-patterns-readme
update my-patterns instructions
2023-01-18 17:10:08 +01:00
Felix Roos 9923bcd16f update my-patterns instructions 2023-01-18 17:01:27 +01:00
urswilke 0179ec051f Revert "run: npm test"
This reverts commit 00709539e9bdfbc1404e984f28453f0a54b5638e.
2023-01-16 23:19:46 +01:00
urswilke 05b6353355 move drum patterns in a new file 2023-01-16 23:11:17 +01:00
urswilke 47c6ed8829 Revert "import patterns created by new method in:"
This reverts commit b266e975755e035b85c0869a30219c80a84db856.
2023-01-16 23:06:43 +01:00
urswilke ff9ebd64e3 run: npm test 2023-01-16 22:41:12 +01:00
urswilke cd2b098b81 import patterns created by new method in:
https://github.com/urswilke/read_beats/commit/ecd8c03b5e29544819831459f4b4ac48d954640f
2023-01-16 22:41:12 +01:00
Felix Roos 7000a4cead Merge pull request #377 from tidalcycles/draw-fixes
Draw fixes
2023-01-15 23:19:42 +01:00
Felix Roos 5aa983b45b do less work when not drawing 2023-01-15 23:11:49 +01:00
Felix Roos 800989419b catch all query errors by default 2023-01-15 22:50:56 +01:00
Felix Roos 362679954a Merge pull request #372 from tidalcycles/local-draw
improve new draw logic
2023-01-14 11:07:07 +01:00
Felix Roos 2732d64beb fix: another dependency array 2023-01-14 10:58:30 +01:00
Felix Roos 88684d6d9e fix dependency array 2023-01-14 10:48:52 +01:00
Felix Roos 9afddf0850 fix sentence 2023-01-14 01:30:56 +01:00
Felix Roos a2d7d6e81f fix: mini repl could eval before modules are ready 2023-01-14 01:27:22 +01:00
Felix Roos 49bcb5e19f fix minor bugs 2023-01-13 16:35:24 +01:00
Felix Roos 3744ea6218 fix: warnings 2023-01-13 15:17:23 +01:00
Felix Roos b2c6d87633 MiniRepl: add punchcard flag for implicit vis
+ reintroduce editPattern
+ add punchcards to mini-notation.mdx
2023-01-13 15:13:39 +01:00
Felix Roos e4f538b674 fix option handling 2023-01-13 13:00:11 +01:00
Felix Roos ea0e0b4396 reset canvas when pattern changes
+ rename noteroll -> punchcard
2023-01-13 12:57:35 +01:00
Felix Roos 1ac784dc7a localize draw logic 2023-01-13 12:03:34 +01:00
Felix Roos d980c9ca4c Merge branch 'main' into docs 2023-01-13 01:52:36 +01:00
Felix Roos 6a2a60df0d hotfix: tests 2023-01-12 17:43:20 +01:00
Felix Roos ee9b89b6d5 add more synonyms 2023-01-12 17:40:58 +01:00
Felix Roos 68cc109933 burn down that undocumented list 2023-01-12 17:25:01 +01:00
Felix Roos 1015bb0fdc Merge pull request #369 from tidalcycles/docs
document more functions + change arp join
2023-01-12 14:40:58 +01:00
Felix Roos 7da94dd109 reformat headings
+ add another note about single quotes
2023-01-12 14:22:41 +01:00
Felix Roos 171808cf2c add note about single quote strings 2023-01-12 14:19:18 +01:00
Felix Roos 78d14cdfea automatically print synonyms in doc 2023-01-12 13:10:22 +01:00
Felix Roos 19982a2ffb remove unused package 2023-01-12 13:00:22 +01:00
Felix Roos 79904560fc fix: snapshot 2023-01-12 12:48:05 +01:00
Felix Roos 28aee1c58d fix polymeter doc 2023-01-12 12:43:41 +01:00
Felix Roos d4512f360b fix: double slash 2023-01-12 12:40:20 +01:00
Felix Roos 48d0ffe868 add script that finds all undocumented exports 2023-01-12 12:35:04 +01:00
Felix Roos 62af12f3b9 remove experimental class properties 2023-01-12 12:32:27 +01:00
Felix Roos efa4bfef2f fix snapshot 2023-01-12 02:23:06 +01:00
Felix Roos fc3ead4c01 Merge branch 'main' into docs 2023-01-12 02:20:16 +01:00
Felix Roos ca9c813b40 format 2023-01-12 02:18:09 +01:00
Felix Roos e3b0f47c40 hide internals for now 2023-01-12 02:15:42 +01:00
Felix Roos bf13e42092 update snap 2023-01-12 01:57:14 +01:00
Felix Roos 4345fda9bc more doc 2023-01-12 01:56:46 +01:00
Felix Roos 4059e9faa9 more doc 2023-01-12 00:10:58 +01:00
Felix Roos 95fcce1bcf fix: snapshot 2023-01-11 23:38:40 +01:00
Felix Roos 86cde9716a arpWith example 2023-01-11 23:37:59 +01:00
Felix Roos 6e315a50ed breaking: arp use innerJoin instead of squeezeJoin 2023-01-11 23:28:17 +01:00
Felix Roos 500c0b6534 doc: arp + arpWith 2023-01-11 23:26:03 +01:00
Felix Roos db75dca118 doc: split accumulation and conditions 2023-01-11 23:05:17 +01:00
Felix Roos d5f37e2cbe add internals doc 2023-01-11 22:48:47 +01:00
Felix Roos a120a9895b fix: right sidebar width 2023-01-11 22:48:33 +01:00
Felix Roos e9c1db020f hotfix: remove mocked .out 2023-01-11 22:11:22 +01:00
Felix Roos 0974540e5f fix: typo 2023-01-10 00:21:54 +01:00
178 changed files with 23170 additions and 51232 deletions
+1
View File
@@ -15,3 +15,4 @@ vite.config.js
!**/*.mjs
**/*.tsx
**/*.ts
**/*.json
+3 -2
View File
@@ -9,8 +9,9 @@
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [],
"plugins": ["import"],
"rules": {
"no-unused-vars": ["warn", { "destructuredArrayIgnorePattern": ".", "ignoreRestSiblings": false }]
"no-unused-vars": ["warn", { "destructuredArrayIgnorePattern": ".", "ignoreRestSiblings": false }],
"import/no-extraneous-dependencies": ["error", {"devDependencies": true}]
}
}
+6 -3
View File
@@ -22,15 +22,18 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v2
- uses: pnpm/action-setup@v2
with:
version: 7
- uses: actions/setup-node@v3
with:
node-version: 18
cache: "npm"
cache: "pnpm"
- name: Install Dependencies
run: npm ci && cd website && npm ci
run: pnpm install
- name: Build
run: npm run build
run: pnpm build
- name: Setup Pages
uses: actions/configure-pages@v2
+8 -5
View File
@@ -11,11 +11,14 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: pnpm/action-setup@v2
with:
version: 7
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: "npm"
- run: npm install
- run: npm run format-check
- run: npm run lint
- run: npm test
cache: 'pnpm'
- run: pnpm install
- run: pnpm run format-check
- run: pnpm run lint
- run: pnpm test
+3 -1
View File
@@ -37,4 +37,6 @@ talk/public/EmuSP12
talk/public/samples
server/samples/old
repl/stats.html
coverage
coverage
public/icons/apple-splash-*
dev-dist
+3 -1
View File
@@ -6,4 +6,6 @@
**/dist
packages/mini/krill-parser.js
packages/xen/tunejs.js
paper
paper
pnpm-lock.yaml
pnpm-workspace.yaml
+12 -11
View File
@@ -58,23 +58,22 @@ To fix a bug that has been reported,
## Write Tests
There are still many tests that have not been written yet! Reading and writing tests is a great opportunity to get familiar with the codebase.
You can find the tests in each package in the `test` folder. To run all tests, run `npm test` from the root folder.
You can find the tests in each package in the `test` folder. To run all tests, run `pnpm test` from the root folder.
## Project Setup
To get the project up and running for development, make sure you have installed:
- git
- node, preferably v16
- [git](https://git-scm.com/)
- [node](https://nodejs.org/en/) >= 18
- [pnpm](https://pnpm.io/) (`npm i pnpm -g`)
then, do the following:
```sh
git clone https://github.com/tidalcycles/strudel.git && cd strudel
npm i # install at root to symlink packages
npx lerna bootstrap # install all dependencies in packages
cd repl && npm i # install repl dependencies
npm run start # start repl
pnpm i # install at root to symlink packages
pnpm start # start repl
```
Those commands might look slightly different for your OS.
@@ -93,9 +92,8 @@ If you use VSCode, you can
## Package Workflow
The project is split into multiple [packages](https://github.com/tidalcycles/strudel/tree/main/packages) with independent versioning.
When you run `npm i` on the root folder, [npm workspaces](https://docs.npmjs.com/cli/v7/using-npm/workspaces) will symlink all packages
in the `node_modules` folder. This will allow any js file to import `@strudel.cycles/<package-name>` to get the local version,
which allows developing multiple packages at the same time
When you run `pnpm i` on the root folder, [pnpm workspaces](https://pnpm.io/workspaces) will install all dependencies of all subpackages. This will allow any js file to import `@strudel.cycles/<package-name>` to get the local version,
allowing to develop multiple packages at the same time.
## Package Publishing
@@ -106,12 +104,15 @@ npm login
npx lerna publish
```
To manually publish a single package, increase the version in the `package.json`, then run `pnpm publish`.
Important: Always publish with `pnpm`, as `npm` does not support overriding main files in `publishConfig`, which is done in all the packages.
### New Packages
To add a new package, you have to publish it manually the first time, using:
```sh
cd packages/<package-name> && npm publish --access public
cd packages/<package-name> && pnpm publish --access public
```
## Have Fun
+1
View File
@@ -7,6 +7,7 @@ An experiment in making a [Tidal](https://github.com/tidalcycles/tidal/) using w
- Try it here: <https://strudel.tidalcycles.org/>
- Docs: <https://strudel.tidalcycles.org/learn/>
- Technical Blog Post: <https://loophole-letters.vercel.app/strudel>
- 1 Year of Strudel Blog Post: <https://loophole-letters.vercel.app/strudel1year>
## Running Locally
+17
View File
@@ -0,0 +1,17 @@
// this barrel export is currently only used to find undocumented exports
export * from './packages/core/index.mjs';
export * from './packages/csound/index.mjs';
export * from './packages/embed/index.mjs';
export * from './packages/eval/index.mjs';
export * from './packages/midi/index.mjs';
export * from './packages/mini/index.mjs';
export * from './packages/osc/index.mjs';
export * from './packages/react/index.mjs';
export * from './packages/serial/index.mjs';
export * from './packages/soundfonts/index.mjs';
export * from './packages/tonal/index.mjs';
export * from './packages/tone/index.mjs';
export * from './packages/transpiler/index.mjs';
export * from './packages/webaudio/index.mjs';
export * from './packages/webdirt/index.mjs';
export * from './packages/xen/index.mjs';
+3 -1
View File
@@ -2,5 +2,7 @@
"packages": [
"packages/*"
],
"version": "independent"
"version": "independent",
"npmClient": "pnpm",
"useWorkspaces": true
}
+52 -10
View File
@@ -3,18 +3,60 @@
This directory can be used to save your own patterns, which then get
made into a pattern swatch.
0. fork and clone the strudel repository
1. run `npm run setup` in the strudel folder
1. Save one or more .txt files in this folder
2. run `npm run repl` in the top-level strudel folder
3. open `http://localhost:3000/swatch/` !
Example: <https://felixroos.github.io/strudel/swatch/>
## deploy
1. in your fork, go to settings -> pages and select "Github Actions" as source
2. edit `website/public/CNAME` to contain `<your-username>.github.io/strudel`
3. edit `website/astro.config.mjs` to use site: `https://<your-username>.github.io` and base `/strudel`
4. go to Actions -> `Build and Deploy` and click `Run workflow`
5. view your patterns at `<your-username>.github.io/strudel/swatch/`
### 1. fork the [strudel repo on github](https://github.com/tidalcycles/strudel.git)
### 2. clone your fork to your machine `git clone https://github.com/<your-username>/strudel.git strudel && cd strudel`
### 3. create a separate branch like `git branch patternuary && git checkout patternuary`
### 4. save one or more .txt files in the my-patterns folder
### 5. edit `website/public/CNAME` to contain `<your-username>.github.io/strudel`
### 6. edit `website/astro.config.mjs` to use site: `https://<your-username>.github.io` and base `/strudel`, like this
```js
export default defineConfig({
/* ... rest of config ... */
site: 'https://<your-username>.github.io',
base: '/strudel',
});
```
### 7. commit & push the changes
```sh
git add . && git commit -m "site config" && git push --set-upstream origin
```
### 8. deploy to github pages
- go to settings -> pages and select "Github Actions" as source
- go to settings -> environments -> github-pages and press the edit button next to `main` and type in `patternuary` (under "Deployment branches")
- go to Actions -> `Build and Deploy` and click `Run workflow` with branch `patternuary`
### 9. view your patterns at `<your-username>.github.io/strudel/swatch/`
Alternatively, github pages allows you to use a custom domain, like https://mycooldomain.org/swatch/. [See their documentation for details](https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site).
### 10. optional: automatic deployment
If you want to automatically deploy your site on push, go to `deploy.yml` and change `workflow_dispatch` to `push`.
## running locally
- install dependencies with `npm run setup`
- run dev server with `npm run repl` and open `http://localhost:3000/strudel/swatch/`
## tests fail?
Your tests might fail if the code does not follow prettiers format.
In that case, run `npm run codeformat`. To disable that, remove `npm run format-check` from `test.yml`
## updating your fork
To update your fork, you can pull the main branch and merge it into your `patternuary` branch.
-20806
View File
File diff suppressed because it is too large Load Diff
+24 -14
View File
@@ -4,30 +4,30 @@
"private": true,
"description": "Port of tidalcycles to javascript",
"scripts": {
"setup": "pnpm i",
"pretest": "npm run jsdoc-json",
"prebuild": "npm run jsdoc-json",
"test": "vitest run --version",
"test-ui": "vitest --ui",
"test-coverage": "vitest --coverage",
"bootstrap": "lerna bootstrap",
"setup": "npm i && npm run jsdoc-json && npm run bootstrap && cd website && npm i",
"snapshot": "vitest run -u --silent",
"repl": "cd website && npm run dev",
"osc": "cd packages/osc && npm run server",
"build": "cd website && npm run build",
"prestart": "npm run jsdoc-json",
"test": "npm run pretest && vitest run --version",
"test-ui": "npm run pretest && vitest --ui",
"test-coverage": "npm run pretest && vitest --coverage",
"snapshot": "npm run pretest && vitest run -u --silent",
"repl": "npm run prestart && cd website && npm run dev",
"start": "npm run prestart && cd website && npm run dev",
"dev": "npm run prestart && cd website && npm run dev",
"build": "npm run prebuild && cd website && npm run build",
"preview": "cd website && npm run preview",
"osc": "cd packages/osc && npm run server",
"deploy": "NODE_DEBUG=gh-pages gh-pages -d out",
"jsdoc": "jsdoc packages/ -c jsdoc.config.json",
"jsdoc-json": "jsdoc packages/ --template ./node_modules/jsdoc-json --destination doc.json -c jsdoc.config.json",
"lint": "eslint . --ext mjs,js --quiet",
"codeformat": "prettier --write .",
"format-check": "prettier --check .",
"report-undocumented": "npm run jsdoc-json && node undocumented.mjs > undocumented.json",
"check": "npm run format-check && npm run lint && npm run test",
"iclc": "cd paper && pandoc --template=pandoc/iclc.html --citeproc --number-sections iclc2023.md -o iclc2023.html && pandoc --template=pandoc/iclc.latex --citeproc --number-sections iclc2023.md -o iclc2023.pdf"
},
"workspaces": [
"packages/*"
],
"repository": {
"type": "git",
"url": "git+https://github.com/tidalcycles/strudel.git"
@@ -45,11 +45,22 @@
"url": "https://github.com/tidalcycles/strudel/issues"
},
"homepage": "https://strudel.tidalcycles.org",
"dependencies": {
"dependency-tree": "^9.0.0",
"vitest": "^0.25.7",
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/mini": "workspace:*",
"@strudel.cycles/tonal": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"@strudel.cycles/xen": "workspace:*"
},
"devDependencies": {
"@vitest/ui": "^0.25.7",
"c8": "^7.12.0",
"canvas": "^2.11.0",
"eslint": "^8.28.0",
"eslint-plugin-import": "^2.27.5",
"events": "^3.3.0",
"gh-pages": "^4.0.0",
"jsdoc": "^3.6.10",
@@ -57,7 +68,6 @@
"jsdoc-to-markdown": "^7.1.1",
"lerna": "^4.0.0",
"prettier": "^2.8.1",
"rollup-plugin-visualizer": "^5.8.1",
"vitest": "^0.25.7"
"rollup-plugin-visualizer": "^5.8.1"
}
}
+4 -3
View File
@@ -33,6 +33,7 @@ b: 3/2 - 7/4
c: 7/4 - 2
```
- [play with @strudel.cycles/core on codesandbox](https://codesandbox.io/s/strudel-core-test-qmz6qr?file=/src/index.js).
- [open color pattern example](https://raw.githack.com/tidalcycles/strudel/package-examples/packages/core/examples/canvas.html)
- [open minimal repl example](https://raw.githack.com/tidalcycles/strudel/package-examples/packages/core/examples/metro.html)
- [play with @strudel.cycles/core on codesandbox](https://codesandbox.io/s/strudel-core-test-forked-9ywhv7?file=/src/index.js).
- [open color pattern example](https://raw.githack.com/tidalcycles/strudel/main/packages/core/examples/canvas.html)
- [open minimal repl example](https://raw.githack.com/tidalcycles/strudel/main/packages/core/examples/vanilla.html)
- [open minimal vite example](./examples/vite-vanilla-repl/)
+4 -4
View File
@@ -22,11 +22,11 @@ Pattern.prototype.animate = function ({ callback, sync = false, smear = 0.5 } =
ctx.fillStyle = clearColor;
ctx.fillRect(0, 0, ww, wh);
frame.forEach((f) => {
let { x, y, w, h, s, r, a = 0, fill = 'darkseagreen' } = f.value;
let { x, y, w, h, s, r, angle = 0, fill = 'darkseagreen' } = f.value;
w *= ww;
h *= wh;
if (r !== undefined && a !== undefined) {
const radians = a * 2 * Math.PI;
if (r !== undefined && angle !== undefined) {
const radians = angle * 2 * Math.PI;
const [cx, cy] = [(ww - w) / 2, (wh - h) / 2];
x = cx + Math.cos(radians) * r * cx;
y = cy + Math.sin(radians) * r * cy;
@@ -51,7 +51,7 @@ Pattern.prototype.animate = function ({ callback, sync = false, smear = 0.5 } =
return silence;
};
export const { x, y, w, h, a, r, fill, smear } = createParams('x', 'y', 'w', 'h', 'a', 'r', 'fill', 'smear');
export const { x, y, w, h, angle, r, fill, smear } = createParams('x', 'y', 'w', 'h', 'angle', 'r', 'fill', 'smear');
export const rescale = register('rescale', function (f, pat) {
return pat.mul(x(f).w(f).y(f).h(f));
+49 -44
View File
@@ -83,8 +83,7 @@ const generic_params = [
*/
['f', 'amp', 'like @gain@, but linear.'],
/**
* A pattern of numbers to specify the attack time of an envelope applied to each sample.
* [More info about envelopes](/learn/synths-samples-effects/#envelope)
* Amplitude envelope attack time: Specifies how long it takes for the sound to reach its peak value, relative to the onset.
*
* @name attack
* @param {number | Pattern} attack time in seconds.
@@ -92,11 +91,7 @@ const generic_params = [
* note("c3 e3").attack("<0 .1 .5>")
*
*/
[
'f',
'attack',
'a pattern of numbers to specify the attack time (in seconds) of an envelope applied to each sample.',
],
['f', 'attack'],
/**
* Select the sound bank to use. To be used together with `s`. The bank name (+ "_") will be prepended to the value of `s`.
@@ -110,8 +105,8 @@ const generic_params = [
['f', 'bank', 'selects sound bank to use'],
/**
* Gain envelope decay time = the time it takes after the attack time to reach the sustain level.
* [More info about envelopes](/learn/synths-samples-effects/#envelope)
* Amplitude envelope decay time: the time it takes after the attack time to reach the sustain level.
* Note that the decay is only audible if the sustain value is lower than 1.
*
* @name decay
* @param {number | Pattern} time decay time in seconds
@@ -121,7 +116,7 @@ const generic_params = [
*/
['f', 'decay', ''],
/**
* Gain envelope sustain level. [More info about envelopes](/learn/synths-samples-effects/#envelope)
* Amplitude envelope sustain level: The level which is reached after attack / decay, being sustained until the offset.
*
* @name sustain
* @param {number | Pattern} gain sustain level between 0 and 1
@@ -131,7 +126,7 @@ const generic_params = [
*/
['f', 'sustain', ''],
/**
* Gain envelope release time. [More info about envelopes](/learn/synths-samples-effects/#envelope)
* Amplitude envelope release time: The time it takes after the offset to go from sustain level to zero.
*
* @name release
* @param {number | Pattern} time release time in seconds
@@ -151,30 +146,30 @@ const generic_params = [
],
// TODO: in tidal, it seems to be normalized
/**
* Sets the center frequency of the band-pass filter.
* Sets the center frequency of the **b**and-**p**ass **f**ilter.
*
* @name bandf
* @name bpf
* @param {number | Pattern} frequency center frequency
* @synonyms bpf
* @synonyms bandf
* @example
* s("bd sd,hh*3").bandf("<1000 2000 4000 8000>")
* s("bd sd,hh*3").bpf("<1000 2000 4000 8000>")
*
*/
['f', 'bandf', 'A pattern of numbers from 0 to 1. Sets the center frequency of the band-pass filter.'],
['f', 'bpf', ''],
['f', 'bandf', 'A pattern of numbers from 0 to 1. Sets the center frequency of the band-pass filter.'],
// TODO: in tidal, it seems to be normalized
/**
* Sets the q-factor of the band-pass filter
* Sets the **b**and-**p**ass **q**-factor (resonance)
*
* @name bandq
* @name bpq
* @param {number | Pattern} q q factor
* @synonyms bpq
* @synonyms bandq
* @example
* s("bd sd").bandf(500).bandq("<0 1 2 3>")
* s("bd sd").bpf(500).bpq("<0 1 2 3>")
*
*/
['f', 'bandq', 'a pattern of anumbers from 0 to 1. Sets the q-factor of the band-pass filter.'],
['f', 'bpq', ''],
['f', 'bandq', 'a pattern of anumbers from 0 to 1. Sets the q-factor of the band-pass filter.'],
/**
* a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample.
*
@@ -282,50 +277,49 @@ const generic_params = [
'In the style of classic drum-machines, `cut` will stop a playing sample as soon as another samples with in same cutgroup is to be played. An example would be an open hi-hat followed by a closed one, essentially muting the open.',
],
/**
* Applies the cutoff frequency of the low-pass filter.
* Applies the cutoff frequency of the **l**ow-**p**ass **f**ilter.
*
* @name cutoff
* @name lpf
* @param {number | Pattern} frequency audible between 0 and 20000
* @synonyms lpf
* @synonyms cutoff
* @example
* s("bd sd,hh*3").cutoff("<4000 2000 1000 500 200 100>")
* s("bd sd,hh*3").lpf("<4000 2000 1000 500 200 100>")
*
*/
['f', 'cutoff', 'a pattern of numbers from 0 to 1. Applies the cutoff frequency of the low-pass filter.'],
['f', 'lpf'],
['f', 'cutoff', 'a pattern of numbers from 0 to 1. Applies the cutoff frequency of the low-pass filter.'],
/**
* Applies the cutoff frequency of the high-pass filter.
* Applies the cutoff frequency of the **h**igh-**p**ass **f**ilter.
*
* @name hcutoff
* @name hpf
* @param {number | Pattern} frequency audible between 0 and 20000
* @synonyms hpf
* @synonyms hcutoff
* @example
* s("bd sd,hh*4").hcutoff("<4000 2000 1000 500 200 100>")
* s("bd sd,hh*4").hpf("<4000 2000 1000 500 200 100>")
*
*/
['f', 'hcutoff', ''],
['f', 'hpf', ''],
['f', 'hcutoff', ''],
/**
* Applies the resonance of the high-pass filter.
* Controls the **h**igh-**p**ass **q**-value.
*
* @name hresonance
* @name hpq
* @param {number | Pattern} q resonance factor between 0 and 50
* @synonyms hpq
* @synonyms hresonance
* @example
* s("bd sd,hh*4").hcutoff(2000).hresonance("<0 10 20 30>")
* s("bd sd,hh*4").hpf(2000).hpq("<0 10 20 30>")
*
*/
['f', 'hpq', ''],
['f', 'hresonance', ''],
// TODO: add hpq synonym
['f', 'hpq', ''],
/**
* Applies the cutoff frequency of the low-pass filter.
* Controls the **l**ow-**p**ass **q**-value.
*
* @name resonance
* @name lpq
* @param {number | Pattern} q resonance factor between 0 and 50
* @synonyms lpq
* @synonyms resonance
* @example
* s("bd sd,hh*4").cutoff(2000).resonance("<0 10 20 30>")
* s("bd sd,hh*4").lpf(2000).lpq("<0 10 20 30>")
*
*/
['f', 'lpq'],
@@ -621,14 +615,15 @@ const generic_params = [
/**
* Sets the room size of the reverb, see {@link room}.
*
* @name size
* @name roomsize
* @synonyms size
* @param {number | Pattern} size between 0 and 10
* @example
* s("bd sd").room(.8).size("<0 1 2 4 8>")
* s("bd sd").room(.8).roomsize("<0 1 2 4 8>")
*
*/
// TODO: find out why :
// s("bd sd").room(.8).size("<0 .2 .4 .6 .8 [1,0]>").osc()
// s("bd sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc()
// .. does not work. Is it because room is only one effect?
[
'f',
@@ -803,6 +798,16 @@ const generic_params = [
['f', 'uid', ''],
['f', 'val', ''],
['f', 'cps', ''],
/**
* If set to 1, samples will be cut to the duration of their event.
* In tidal, this would be done with legato, which [is about to land in strudel too](https://github.com/tidalcycles/strudel/issues/111)
*
* @name clip
* @param {number | Pattern} active 1 or 0
* @example
* note("c a f e ~").s("piano").clip(1)
*
*/
['f', 'clip', ''],
];
+3 -6
View File
@@ -8,13 +8,10 @@ import createClock from './zyklus.mjs';
import { logger } from './logger.mjs';
export class Cyclist {
worker;
pattern;
started = false;
cps = 1; // TODO
getTime;
phase = 0;
constructor({ interval, onTrigger, onToggle, onError, getTime, latency = 0.1 }) {
this.started = false;
this.cps = 1; // TODO
this.phase = 0;
this.getTime = getTime;
this.onToggle = onToggle;
this.latency = latency;
+6
View File
@@ -59,3 +59,9 @@ export const cleanupDraw = (clearScreen = true) => {
clearInterval(window.strudelScheduler);
}
};
Pattern.prototype.onPaint = function (onPaint) {
// this is evil! TODO: add pattern.context
this.context = { onPaint };
return this;
};
+1 -1
View File
@@ -46,7 +46,7 @@ const _bjork = function (n, x) {
return Math.min(ons, offs) <= 1 ? [n, x] : _bjork(...(ons > offs ? left(n, x) : right(n, x)));
};
const bjork = function (ons, steps) {
export const bjork = function (ons, steps) {
const offs = steps - ons;
const x = Array(ons).fill([1]);
const y = Array(offs).fill([0]);
+2 -2
View File
@@ -1,13 +1,13 @@
<input
type="text"
id="text"
value="cat('a', 'b')"
value="seq('a', ['b', 'c'])"
style="width: 100%; font-size: 2em; outline: none; margin-bottom: 10px"
spellcheck="false"
/>
<div id="output"></div>
<script type="module">
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.0.2');
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8');
Object.assign(window, strudel); // assign all strudel functions to global scope to use with eval
const input = document.getElementById('text');
const getEvents = () => {
+2 -2
View File
@@ -2,13 +2,13 @@
<input
type="text"
id="text"
value="cat('orange', 'indigo')"
value="seq('tomato', 'indigo', ['white', 'steelblue']).fast(4)"
style="width: 100%; font-size: 2em; background: black; color: white; outline: none; position: absolute; top: 0"
spellcheck="false"
/>
<canvas id="canvas"></canvas>
<script type="module">
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.0.2');
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8');
// this adds all strudel functions to the global scope, to be used by eval
Object.assign(window, strudel);
// setup elements
-90
View File
@@ -1,90 +0,0 @@
<div style="position: absolute; bottom: 0; right: 0; padding: 4px; width: 100vw; display: flex">
<button id="start" style="font-size: 2em">start</button>
<button id="stop" style="font-size: 2em">stop</button>
<button id="slower" style="font-size: 2em">slower</button>
<button id="faster" style="font-size: 2em">faster</button>
</div>
<textarea
style="font-size: 2em; background: #052b49; color: #fff; height: 100%; width: 100%; outline: none; border: 0"
id="text"
spellcheck="false"
>
Loading...</textarea
>
<script type="module">
document.body.style = 'margin: 0';
import * as strudel from '@strudel.cycles/core';
const { cat, State, TimeSpan, Scheduler, getPlayableNoteValue, getFreq } = strudel;
Object.assign(window, strudel); // add strudel to eval scope
const ctx = new AudioContext();
const scheduler = new Scheduler({
// audioContext: getAudioContext(),
interval: 0.1,
onTrigger: (hap, time, duration) => {
const freq = getFrequency(hap);
const osc = ctx.createOscillator();
const gain = 0.2;
osc.frequency.value = freq;
osc.type = 'triangle';
const onset = ctx.currentTime + time;
const offset = onset + duration;
const attack = 0.05;
const release = 0.05;
osc.start(onset);
osc.stop(offset + release);
const g = ctx.createGain();
g.gain.setValueAtTime(gain, onset);
g.gain.linearRampToValueAtTime(gain, onset + attack);
g.gain.setValueAtTime(gain, offset - release);
g.gain.linearRampToValueAtTime(0, offset);
osc.connect(g);
g.connect(ctx.destination);
},
});
let initialCode = `stack('c4','e4',cat('g4','a4','b4','a4'))
.add(cat(0,1,2,3,4,3,2,1).slow(8))
.fast(2)
.cps(tri.range(1,8).slow(32))`;
try {
const base64 = decodeURIComponent(window.location.href.split('#')[1]);
initialCode = atob(base64);
} catch (err) {
console.warn('failed to decode', err);
}
const input = document.getElementById('text');
input.value = initialCode;
const evaluate = () => {
try {
const pattern = eval(input.value);
scheduler.setPattern(pattern);
window.location.hash = '#' + encodeURIComponent(btoa(input.value)); // update url hash
} catch (err) {
console.warn(err);
}
};
evaluate();
input.addEventListener('input', () => evaluate());
document.getElementById('start').addEventListener('click', async () => {
await ctx.resume();
scheduler.start();
});
document.getElementById('stop').addEventListener('click', () => scheduler.stop());
document.getElementById('slower').addEventListener('click', () => scheduler.setCps(scheduler.cps - 0.1));
document.getElementById('faster').addEventListener('click', () => scheduler.setCps(scheduler.cps + 0.1));
</script>
<!--
sequence(1,2).mul(55/2) // frequencies
.mul(slowcat(1,2))
.mul(slowcat(1,3/2,4/3,5/3).slow(8))
.fast(3)
.freq()
.velocity(.5)
.s('sawtooth')
.cutoff(800)
.out()
-->
-44
View File
@@ -1,44 +0,0 @@
<input
type="text"
id="text"
value="seq('c3','eb3','g3').note().s('sawtooth').out()"
style="width: 100%; font-size: 2em; outline: none; margin-bottom: 10px"
spellcheck="false"
/>
<button id="start">play</button>
<div id="output"></div>
<script type="module">
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@latest');
const controls = await import('https://cdn.skypack.dev/@strudel.cycles/core@latest/controls.mjs');
const { getAudioContext, Scheduler } = await import('https://cdn.skypack.dev/@strudel.cycles/webaudio@latest');
let scheduler;
const audioContext = getAudioContext();
const latency = 0.2;
Object.assign(window, strudel);
Object.assign(window, controls.default);
scheduler = new Scheduler({
audioContext,
interval: 0.1,
latency,
onEvent: (hap) => {
if (!hap.context.onTrigger) {
console.warn('no output chosen. use one of .out() .webdirt() .osc()');
}
},
});
let started;
document.getElementById('start').addEventListener('click', async () => {
const code = document.getElementById('text').value;
const pattern = eval(code);
const events = pattern._firstCycleValues;
console.log(code, '->', events);
scheduler.setPattern(pattern);
if (!started) {
scheduler.start();
started = true;
}
});
</script>
+16 -29
View File
@@ -6,46 +6,36 @@
<title>Buildless Vanilla Strudel REPL</title>
</head>
<body style="margin: 0; background: #222">
<div style="display: grid; height: 100vh">
<div style="display: grid; height: 100vh; grid-template-rows: 32px auto">
<button id="start" style="width: 100vw; height: 32px">evaluate</button>
<textarea
id="text"
style="font-size: 2em; border: 0; color: white; background: transparent; outline: none; padding: 20px"
spellcheck="false"
></textarea>
</div>
<button
id="start"
style="
position: absolute;
border-radius: 10px;
top: 20px;
right: 20px;
padding: 20px;
border: 2px solid white;
background: transparent;
color: white;
cursor: pointer;
"
>
evaluate
</button>
<div id="output"></div>
<script type="module">
import { controls, repl, evalScope } from 'https://cdn.skypack.dev/@strudel.cycles/core@0.3.2';
import { mini } from 'https://cdn.skypack.dev/@strudel.cycles/mini@0.3.2';
import { transpiler } from 'https://cdn.skypack.dev/@strudel.cycles/transpiler@0.3.2';
import { getAudioContext, webaudioOutput } from 'https://cdn.skypack.dev/@strudel.cycles/webaudio@0.3.3';
import { controls, repl, evalScope } from 'https://cdn.skypack.dev/@strudel.cycles/core@0.6.8';
import { mini } from 'https://cdn.skypack.dev/@strudel.cycles/mini@0.6.0';
import { transpiler } from 'https://cdn.skypack.dev/@strudel.cycles/transpiler@0.6.0';
import {
getAudioContext,
webaudioOutput,
initAudioOnFirstClick,
} from 'https://cdn.skypack.dev/@strudel.cycles/webaudio@0.6.0';
initAudioOnFirstClick();
const ctx = getAudioContext();
const input = document.getElementById('text');
input.innerHTML = getTune();
evalScope(
controls,
import('https://cdn.skypack.dev/@strudel.cycles/core@0.3.2'),
import('https://cdn.skypack.dev/@strudel.cycles/mini@0.3.2'),
import('https://cdn.skypack.dev/@strudel.cycles/tonal@0.3.3'),
import('https://cdn.skypack.dev/@strudel.cycles/webaudio@0.3.3'),
import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8'),
import('https://cdn.skypack.dev/@strudel.cycles/mini@0.6.0'),
import('https://cdn.skypack.dev/@strudel.cycles/tonal@0.6.0'),
import('https://cdn.skypack.dev/@strudel.cycles/webaudio@0.6.0'),
);
const { evaluate } = repl({
@@ -53,10 +43,7 @@
getTime: () => ctx.currentTime,
transpiler,
});
document.getElementById('start').addEventListener('click', () => {
ctx.resume();
evaluate(input.value);
});
document.getElementById('start').addEventListener('click', () => evaluate(input.value));
function getTune() {
return `await samples('github:tidalcycles/Dirt-Samples/master')
@@ -1,12 +1,12 @@
import { controls, repl, evalScope, setStringParser } from '@strudel.cycles/core';
import { mini } from '@strudel.cycles/mini';
import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio';
// import { transpiler } from '@strudel.cycles/transpiler';
import { controls, repl, evalScope } from '@strudel.cycles/core';
import { getAudioContext, webaudioOutput, initAudioOnFirstClick } from '@strudel.cycles/webaudio';
import { transpiler } from '@strudel.cycles/transpiler';
import tune from './tune.mjs';
const ctx = getAudioContext();
const input = document.getElementById('text');
input.innerHTML = tune;
initAudioOnFirstClick();
evalScope(
controls,
@@ -16,14 +16,13 @@ evalScope(
import('@strudel.cycles/tonal'),
);
setStringParser(mini);
const { evaluate } = repl({
defaultOutput: webaudioOutput,
getTime: () => ctx.currentTime,
// transpiler,
transpiler,
});
document.getElementById('start').addEventListener('click', () => {
ctx.resume();
console.log('eval', input.value);
evaluate(input.value);
});
-885
View File
@@ -1,885 +0,0 @@
{
"name": "vite-vanilla-repl",
"version": "0.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "vite-vanilla-repl",
"version": "0.0.0",
"devDependencies": {
"vite": "^3.2.0"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.13.tgz",
"integrity": "sha512-RY2fVI8O0iFUNvZirXaQ1vMvK0xhCcl0gqRj74Z6yEiO1zAUa7hbsdwZM1kzqbxHK7LFyMizipfXT3JME+12Hw==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.13.tgz",
"integrity": "sha512-+BoyIm4I8uJmH/QDIH0fu7MG0AEx9OXEDXnqptXCwKOlOqZiS4iraH1Nr7/ObLMokW3sOCeBNyD68ATcV9b9Ag==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.13.tgz",
"integrity": "sha512-Cu3SC84oyzzhrK/YyN4iEVy2jZu5t2fz66HEOShHURcjSkOSAVL8C/gfUT+lDJxkVHpg8GZ10DD0rMHRPqMFaQ==",
"dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/android-arm": "0.15.13",
"@esbuild/linux-loong64": "0.15.13",
"esbuild-android-64": "0.15.13",
"esbuild-android-arm64": "0.15.13",
"esbuild-darwin-64": "0.15.13",
"esbuild-darwin-arm64": "0.15.13",
"esbuild-freebsd-64": "0.15.13",
"esbuild-freebsd-arm64": "0.15.13",
"esbuild-linux-32": "0.15.13",
"esbuild-linux-64": "0.15.13",
"esbuild-linux-arm": "0.15.13",
"esbuild-linux-arm64": "0.15.13",
"esbuild-linux-mips64le": "0.15.13",
"esbuild-linux-ppc64le": "0.15.13",
"esbuild-linux-riscv64": "0.15.13",
"esbuild-linux-s390x": "0.15.13",
"esbuild-netbsd-64": "0.15.13",
"esbuild-openbsd-64": "0.15.13",
"esbuild-sunos-64": "0.15.13",
"esbuild-windows-32": "0.15.13",
"esbuild-windows-64": "0.15.13",
"esbuild-windows-arm64": "0.15.13"
}
},
"node_modules/esbuild-android-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.13.tgz",
"integrity": "sha512-yRorukXBlokwTip+Sy4MYskLhJsO0Kn0/Fj43s1krVblfwP+hMD37a4Wmg139GEsMLl+vh8WXp2mq/cTA9J97g==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-android-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.13.tgz",
"integrity": "sha512-TKzyymLD6PiVeyYa4c5wdPw87BeAiTXNtK6amWUcXZxkV51gOk5u5qzmDaYSwiWeecSNHamFsaFjLoi32QR5/w==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-darwin-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.13.tgz",
"integrity": "sha512-WAx7c2DaOS6CrRcoYCgXgkXDliLnFv3pQLV6GeW1YcGEZq2Gnl8s9Pg7ahValZkpOa0iE/ojRVQ87sbUhF1Cbg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-darwin-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.13.tgz",
"integrity": "sha512-U6jFsPfSSxC3V1CLiQqwvDuj3GGrtQNB3P3nNC3+q99EKf94UGpsG9l4CQ83zBs1NHrk1rtCSYT0+KfK5LsD8A==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-freebsd-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.13.tgz",
"integrity": "sha512-whItJgDiOXaDG/idy75qqevIpZjnReZkMGCgQaBWZuKHoElDJC1rh7MpoUgupMcdfOd+PgdEwNQW9DAE6i8wyA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-freebsd-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.13.tgz",
"integrity": "sha512-6pCSWt8mLUbPtygv7cufV0sZLeylaMwS5Fznj6Rsx9G2AJJsAjQ9ifA+0rQEIg7DwJmi9it+WjzNTEAzzdoM3Q==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-32": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.13.tgz",
"integrity": "sha512-VbZdWOEdrJiYApm2kkxoTOgsoCO1krBZ3quHdYk3g3ivWaMwNIVPIfEE0f0XQQ0u5pJtBsnk2/7OPiCFIPOe/w==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.13.tgz",
"integrity": "sha512-rXmnArVNio6yANSqDQlIO4WiP+Cv7+9EuAHNnag7rByAqFVuRusLbGi2697A5dFPNXoO//IiogVwi3AdcfPC6A==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-arm": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.13.tgz",
"integrity": "sha512-Ac6LpfmJO8WhCMQmO253xX2IU2B3wPDbl4IvR0hnqcPrdfCaUa2j/lLMGTjmQ4W5JsJIdHEdW12dG8lFS0MbxQ==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.13.tgz",
"integrity": "sha512-alEMGU4Z+d17U7KQQw2IV8tQycO6T+rOrgW8OS22Ua25x6kHxoG6Ngry6Aq6uranC+pNWNMB6aHFPh7aTQdORQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-mips64le": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.13.tgz",
"integrity": "sha512-47PgmyYEu+yN5rD/MbwS6DxP2FSGPo4Uxg5LwIdxTiyGC2XKwHhHyW7YYEDlSuXLQXEdTO7mYe8zQ74czP7W8A==",
"cpu": [
"mips64el"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-ppc64le": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.13.tgz",
"integrity": "sha512-z6n28h2+PC1Ayle9DjKoBRcx/4cxHoOa2e689e2aDJSaKug3jXcQw7mM+GLg+9ydYoNzj8QxNL8ihOv/OnezhA==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-riscv64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.13.tgz",
"integrity": "sha512-+Lu4zuuXuQhgLUGyZloWCqTslcCAjMZH1k3Xc9MSEJEpEFdpsSU0sRDXAnk18FKOfEjhu4YMGaykx9xjtpA6ow==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-s390x": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.13.tgz",
"integrity": "sha512-BMeXRljruf7J0TMxD5CIXS65y7puiZkAh+s4XFV9qy16SxOuMhxhVIXYLnbdfLrsYGFzx7U9mcdpFWkkvy/Uag==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-netbsd-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.13.tgz",
"integrity": "sha512-EHj9QZOTel581JPj7UO3xYbltFTYnHy+SIqJVq6yd3KkCrsHRbapiPb0Lx3EOOtybBEE9EyqbmfW1NlSDsSzvQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-openbsd-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.13.tgz",
"integrity": "sha512-nkuDlIjF/sfUhfx8SKq0+U+Fgx5K9JcPq1mUodnxI0x4kBdCv46rOGWbuJ6eof2n3wdoCLccOoJAbg9ba/bT2w==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-sunos-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.13.tgz",
"integrity": "sha512-jVeu2GfxZQ++6lRdY43CS0Tm/r4WuQQ0Pdsrxbw+aOrHQPHV0+LNOLnvbN28M7BSUGnJnHkHm2HozGgNGyeIRw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-32": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.13.tgz",
"integrity": "sha512-XoF2iBf0wnqo16SDq+aDGi/+QbaLFpkiRarPVssMh9KYbFNCqPLlGAWwDvxEVz+ywX6Si37J2AKm+AXq1kC0JA==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.13.tgz",
"integrity": "sha512-Et6htEfGycjDrtqb2ng6nT+baesZPYQIW+HUEHK4D1ncggNrDNk3yoboYQ5KtiVrw/JaDMNttz8rrPubV/fvPQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.13.tgz",
"integrity": "sha512-3bv7tqntThQC9SWLRouMDmZnlOukBhOCTlkzNqzGCmrkCJI7io5LLjwJBOVY6kOUlIvdxbooNZwjtBvj+7uuVg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.1"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/is-core-module": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
"dev": true,
"dependencies": {
"has": "^1.0.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"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/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"dev": true
},
"node_modules/postcss": {
"version": "8.4.18",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz",
"integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
}
],
"dependencies": {
"nanoid": "^3.3.4",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/resolve": {
"version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
"dev": true,
"dependencies": {
"is-core-module": "^2.9.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/rollup": {
"version": "2.79.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
"integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
"dev": true,
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=10.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/vite": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-3.2.2.tgz",
"integrity": "sha512-pLrhatFFOWO9kS19bQ658CnRYzv0WLbsPih6R+iFeEEhDOuYgYCX2rztUViMz/uy/V8cLCJvLFeiOK7RJEzHcw==",
"dev": true,
"dependencies": {
"esbuild": "^0.15.9",
"postcss": "^8.4.18",
"resolve": "^1.22.1",
"rollup": "^2.79.1"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
},
"peerDependencies": {
"less": "*",
"sass": "*",
"stylus": "*",
"sugarss": "*",
"terser": "^5.4.0"
},
"peerDependenciesMeta": {
"less": {
"optional": true
},
"sass": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
}
}
}
},
"dependencies": {
"@esbuild/android-arm": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.13.tgz",
"integrity": "sha512-RY2fVI8O0iFUNvZirXaQ1vMvK0xhCcl0gqRj74Z6yEiO1zAUa7hbsdwZM1kzqbxHK7LFyMizipfXT3JME+12Hw==",
"dev": true,
"optional": true
},
"@esbuild/linux-loong64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.13.tgz",
"integrity": "sha512-+BoyIm4I8uJmH/QDIH0fu7MG0AEx9OXEDXnqptXCwKOlOqZiS4iraH1Nr7/ObLMokW3sOCeBNyD68ATcV9b9Ag==",
"dev": true,
"optional": true
},
"esbuild": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.13.tgz",
"integrity": "sha512-Cu3SC84oyzzhrK/YyN4iEVy2jZu5t2fz66HEOShHURcjSkOSAVL8C/gfUT+lDJxkVHpg8GZ10DD0rMHRPqMFaQ==",
"dev": true,
"requires": {
"@esbuild/android-arm": "0.15.13",
"@esbuild/linux-loong64": "0.15.13",
"esbuild-android-64": "0.15.13",
"esbuild-android-arm64": "0.15.13",
"esbuild-darwin-64": "0.15.13",
"esbuild-darwin-arm64": "0.15.13",
"esbuild-freebsd-64": "0.15.13",
"esbuild-freebsd-arm64": "0.15.13",
"esbuild-linux-32": "0.15.13",
"esbuild-linux-64": "0.15.13",
"esbuild-linux-arm": "0.15.13",
"esbuild-linux-arm64": "0.15.13",
"esbuild-linux-mips64le": "0.15.13",
"esbuild-linux-ppc64le": "0.15.13",
"esbuild-linux-riscv64": "0.15.13",
"esbuild-linux-s390x": "0.15.13",
"esbuild-netbsd-64": "0.15.13",
"esbuild-openbsd-64": "0.15.13",
"esbuild-sunos-64": "0.15.13",
"esbuild-windows-32": "0.15.13",
"esbuild-windows-64": "0.15.13",
"esbuild-windows-arm64": "0.15.13"
}
},
"esbuild-android-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.13.tgz",
"integrity": "sha512-yRorukXBlokwTip+Sy4MYskLhJsO0Kn0/Fj43s1krVblfwP+hMD37a4Wmg139GEsMLl+vh8WXp2mq/cTA9J97g==",
"dev": true,
"optional": true
},
"esbuild-android-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.13.tgz",
"integrity": "sha512-TKzyymLD6PiVeyYa4c5wdPw87BeAiTXNtK6amWUcXZxkV51gOk5u5qzmDaYSwiWeecSNHamFsaFjLoi32QR5/w==",
"dev": true,
"optional": true
},
"esbuild-darwin-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.13.tgz",
"integrity": "sha512-WAx7c2DaOS6CrRcoYCgXgkXDliLnFv3pQLV6GeW1YcGEZq2Gnl8s9Pg7ahValZkpOa0iE/ojRVQ87sbUhF1Cbg==",
"dev": true,
"optional": true
},
"esbuild-darwin-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.13.tgz",
"integrity": "sha512-U6jFsPfSSxC3V1CLiQqwvDuj3GGrtQNB3P3nNC3+q99EKf94UGpsG9l4CQ83zBs1NHrk1rtCSYT0+KfK5LsD8A==",
"dev": true,
"optional": true
},
"esbuild-freebsd-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.13.tgz",
"integrity": "sha512-whItJgDiOXaDG/idy75qqevIpZjnReZkMGCgQaBWZuKHoElDJC1rh7MpoUgupMcdfOd+PgdEwNQW9DAE6i8wyA==",
"dev": true,
"optional": true
},
"esbuild-freebsd-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.13.tgz",
"integrity": "sha512-6pCSWt8mLUbPtygv7cufV0sZLeylaMwS5Fznj6Rsx9G2AJJsAjQ9ifA+0rQEIg7DwJmi9it+WjzNTEAzzdoM3Q==",
"dev": true,
"optional": true
},
"esbuild-linux-32": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.13.tgz",
"integrity": "sha512-VbZdWOEdrJiYApm2kkxoTOgsoCO1krBZ3quHdYk3g3ivWaMwNIVPIfEE0f0XQQ0u5pJtBsnk2/7OPiCFIPOe/w==",
"dev": true,
"optional": true
},
"esbuild-linux-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.13.tgz",
"integrity": "sha512-rXmnArVNio6yANSqDQlIO4WiP+Cv7+9EuAHNnag7rByAqFVuRusLbGi2697A5dFPNXoO//IiogVwi3AdcfPC6A==",
"dev": true,
"optional": true
},
"esbuild-linux-arm": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.13.tgz",
"integrity": "sha512-Ac6LpfmJO8WhCMQmO253xX2IU2B3wPDbl4IvR0hnqcPrdfCaUa2j/lLMGTjmQ4W5JsJIdHEdW12dG8lFS0MbxQ==",
"dev": true,
"optional": true
},
"esbuild-linux-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.13.tgz",
"integrity": "sha512-alEMGU4Z+d17U7KQQw2IV8tQycO6T+rOrgW8OS22Ua25x6kHxoG6Ngry6Aq6uranC+pNWNMB6aHFPh7aTQdORQ==",
"dev": true,
"optional": true
},
"esbuild-linux-mips64le": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.13.tgz",
"integrity": "sha512-47PgmyYEu+yN5rD/MbwS6DxP2FSGPo4Uxg5LwIdxTiyGC2XKwHhHyW7YYEDlSuXLQXEdTO7mYe8zQ74czP7W8A==",
"dev": true,
"optional": true
},
"esbuild-linux-ppc64le": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.13.tgz",
"integrity": "sha512-z6n28h2+PC1Ayle9DjKoBRcx/4cxHoOa2e689e2aDJSaKug3jXcQw7mM+GLg+9ydYoNzj8QxNL8ihOv/OnezhA==",
"dev": true,
"optional": true
},
"esbuild-linux-riscv64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.13.tgz",
"integrity": "sha512-+Lu4zuuXuQhgLUGyZloWCqTslcCAjMZH1k3Xc9MSEJEpEFdpsSU0sRDXAnk18FKOfEjhu4YMGaykx9xjtpA6ow==",
"dev": true,
"optional": true
},
"esbuild-linux-s390x": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.13.tgz",
"integrity": "sha512-BMeXRljruf7J0TMxD5CIXS65y7puiZkAh+s4XFV9qy16SxOuMhxhVIXYLnbdfLrsYGFzx7U9mcdpFWkkvy/Uag==",
"dev": true,
"optional": true
},
"esbuild-netbsd-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.13.tgz",
"integrity": "sha512-EHj9QZOTel581JPj7UO3xYbltFTYnHy+SIqJVq6yd3KkCrsHRbapiPb0Lx3EOOtybBEE9EyqbmfW1NlSDsSzvQ==",
"dev": true,
"optional": true
},
"esbuild-openbsd-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.13.tgz",
"integrity": "sha512-nkuDlIjF/sfUhfx8SKq0+U+Fgx5K9JcPq1mUodnxI0x4kBdCv46rOGWbuJ6eof2n3wdoCLccOoJAbg9ba/bT2w==",
"dev": true,
"optional": true
},
"esbuild-sunos-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.13.tgz",
"integrity": "sha512-jVeu2GfxZQ++6lRdY43CS0Tm/r4WuQQ0Pdsrxbw+aOrHQPHV0+LNOLnvbN28M7BSUGnJnHkHm2HozGgNGyeIRw==",
"dev": true,
"optional": true
},
"esbuild-windows-32": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.13.tgz",
"integrity": "sha512-XoF2iBf0wnqo16SDq+aDGi/+QbaLFpkiRarPVssMh9KYbFNCqPLlGAWwDvxEVz+ywX6Si37J2AKm+AXq1kC0JA==",
"dev": true,
"optional": true
},
"esbuild-windows-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.13.tgz",
"integrity": "sha512-Et6htEfGycjDrtqb2ng6nT+baesZPYQIW+HUEHK4D1ncggNrDNk3yoboYQ5KtiVrw/JaDMNttz8rrPubV/fvPQ==",
"dev": true,
"optional": true
},
"esbuild-windows-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.13.tgz",
"integrity": "sha512-3bv7tqntThQC9SWLRouMDmZnlOukBhOCTlkzNqzGCmrkCJI7io5LLjwJBOVY6kOUlIvdxbooNZwjtBvj+7uuVg==",
"dev": true,
"optional": true
},
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"optional": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
"function-bind": "^1.1.1"
}
},
"is-core-module": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
"dev": true,
"requires": {
"has": "^1.0.3"
}
},
"nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"dev": true
},
"path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"dev": true
},
"postcss": {
"version": "8.4.18",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz",
"integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==",
"dev": true,
"requires": {
"nanoid": "^3.3.4",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
}
},
"resolve": {
"version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
"dev": true,
"requires": {
"is-core-module": "^2.9.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
}
},
"rollup": {
"version": "2.79.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
"integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
"dev": true,
"requires": {
"fsevents": "~2.3.2"
}
},
"source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true
},
"supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true
},
"vite": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-3.2.2.tgz",
"integrity": "sha512-pLrhatFFOWO9kS19bQ658CnRYzv0WLbsPih6R+iFeEEhDOuYgYCX2rztUViMz/uy/V8cLCJvLFeiOK7RJEzHcw==",
"dev": true,
"requires": {
"esbuild": "^0.15.9",
"fsevents": "~2.3.2",
"postcss": "^8.4.18",
"resolve": "^1.22.1",
"rollup": "^2.79.1"
}
}
}
}
@@ -11,5 +11,12 @@
},
"devDependencies": {
"vite": "^3.2.0"
},
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/mini": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"@strudel.cycles/tonal": "workspace:*"
}
}
@@ -1,4 +1,4 @@
/* export default `await samples('github:tidalcycles/Dirt-Samples/master')
export default `await samples('github:tidalcycles/Dirt-Samples/master')
stack(
// amen
@@ -29,11 +29,3 @@ stack(
,
n("0 1").s("east").delay(.5).degradeBy(.8).speed(rand.range(.5,1.5))
).reset("<x@7 x(5,8)>")`;
*/
export default `stack(
n("c3 [eb3,g3]")
.delay("<0 .5>")
.delaytime(".16 | .33")
.delayfeedback(".6 | .8")
).sometimes(x=>x.speed("-1"))`;
-65
View File
@@ -1,65 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Buildless Vanilla Strudel REPL</title>
</head>
<body style="margin: 0; background: #222">
<div style="display: grid; height: 100vh">
<textarea
id="text"
style="font-size: 2em; border: 0; color: white; background: transparent; outline: none; padding: 20px"
spellcheck="false"
>
// LOADING</textarea
>
</div>
<button
id="start"
style="
position: absolute;
border-radius: 10px;
top: 20px;
right: 20px;
padding: 20px;
border: 2px solid white;
background: transparent;
color: white;
cursor: pointer;
"
>
evaluate
</button>
<div id="output"></div>
<script type="module">
import { controls, repl, evalScope } from 'https://cdn.skypack.dev/@strudel.cycles/core@0.4.1';
import { transpiler } from 'https://cdn.skypack.dev/@strudel.cycles/transpiler@0.4.1';
const modules = [
import('https://cdn.skypack.dev/@strudel.cycles/core@0.4.1'),
import('https://cdn.skypack.dev/@strudel.cycles/mini@0.4.1'),
];
const input = document.getElementById('text');
Promise.all(modules).then(() => {
input.innerHTML = `note("<c3 [d3 e3]>").cutoff(1000)`;
document.getElementById('start').addEventListener('click', () => {
evaluate(input.value);
});
});
evalScope(controls, ...modules);
const { evaluate } = repl({
defaultOutput: (hap, deadline, duration) => {
console.log(deadline, duration, hap.value);
},
getTime: () => Date.now() / 1000,
transpiler,
beforeEval: (code) => console.log('evaluate', code),
afterEval: (code) => {},
});
</script>
</body>
</html>
-1639
View File
File diff suppressed because it is too large Load Diff
+13 -3
View File
@@ -1,11 +1,17 @@
{
"name": "@strudel.cycles/core",
"version": "0.5.0",
"version": "0.6.8",
"description": "Port of Tidal Cycles to JavaScript",
"main": "index.mjs",
"type": "module",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": {
"test": "vitest run"
"test": "vitest run",
"build": "vite build",
"prepublishOnly": "pnpm build"
},
"repository": {
"type": "git",
@@ -27,5 +33,9 @@
"dependencies": {
"fraction.js": "^4.2.0"
},
"gitHead": "0e26d4e741500f5bae35b023608f062a794905c2"
"gitHead": "0e26d4e741500f5bae35b023608f062a794905c2",
"devDependencies": {
"vite": "^3.2.2",
"vitest": "^0.25.7"
}
}
+254 -62
View File
@@ -23,7 +23,6 @@ export const setStringParser = (parser) => (stringParser = parser);
/** @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.
*
@@ -31,6 +30,7 @@ export class Pattern {
*/
constructor(query) {
this.query = query;
this._Pattern = true; // this property is used to detect if a pattern that fails instanceof Pattern is an instance of another Pattern
}
//////////////////////////////////////////////////////////////////////
@@ -39,8 +39,11 @@ export class Pattern {
/**
* Returns a new pattern, with the function applied to the value of
* each hap. It has the alias {@link Pattern#fmap}.
* @param {Function} func
* @synonyms fmap
* @param {Function} func to to apply to the value
* @returns Pattern
* @example
* "0 1 2".withValue(v => v + 10).log()
*/
withValue(func) {
return new Pattern((state) => this.query(state).map((hap) => hap.withValue(func)));
@@ -53,10 +56,15 @@ export class Pattern {
return this.withValue(func);
}
/**
* Assumes 'this' is a pattern of functions, and given a function to
* resolve wholes, applies a given pattern of values to that
* pattern of functions.
* @param {Function} whole_func
* @param {Function} func
* @returns Pattern
*/
appWhole(whole_func, pat_val) {
// Assumes 'this' is a pattern of functions, and given a function to
// resolve wholes, applies a given pattern of values to that
// pattern of functions.
const pat_func = this;
const query = function (state) {
const hap_funcs = pat_func.query(state);
@@ -326,7 +334,12 @@ export class Pattern {
* silence
*/
queryArc(begin, end) {
return this.query(new State(new TimeSpan(begin, end)));
try {
return this.query(new State(new TimeSpan(begin, end)));
} catch (err) {
logger(`[query]: ${err.message}`, 'error');
return [];
}
}
/**
@@ -679,6 +692,7 @@ export class Pattern {
* Layers the result of the given function(s). Like {@link Pattern.superimpose}, but without the original pattern:
* @name layer
* @memberof Pattern
* @synonyms apply
* @returns Pattern
* @example
* "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*4"
@@ -724,9 +738,10 @@ export class Pattern {
}
/**
* Appends the given pattern(s) to the current pattern. Synonyms: .sequence .fastcat
* Appends the given pattern(s) to the current pattern.
* @name seq
* @memberof Pattern
* @synonyms sequence, fastcat
* @example
* s("hh*2").seq(
* note("c2(3,8)")
@@ -737,9 +752,10 @@ export class Pattern {
}
/**
* Appends the given pattern(s) to the next cycle. Synonym: .slowcat
* Appends the given pattern(s) to the next cycle.
* @name cat
* @memberof Pattern
* @synonyms slowcat
* @example
* s("hh*2").cat(
* note("c2(3,8)")
@@ -822,15 +838,25 @@ Pattern.prototype.collect = function () {
);
};
// applies func to each array of congruent haps
/**
* Selects indices in in stacked notes.
* @example
* note("<[c,eb,g]!2 [c,f,ab] [d,f,ab]>")
* .arpWith(haps => haps[2])
* */
Pattern.prototype.arpWith = function (func) {
return this.collect()
.fmap((v) => reify(func(v)))
.squeezeJoin()
.innerJoin()
.withHap((h) => new Hap(h.whole, h.part, h.value.value, h.combineContext(h.value)));
};
// applies pattern of indices to each array of congruent haps
/**
* Selects indices in in stacked notes.
* @example
* note("<[c,eb,g]!2 [c,f,ab] [d,f,ab]>")
* .arp("0 [0,2] 1 [0,2]").slow(2)
* */
Pattern.prototype.arp = function (pat) {
return this.arpWith((haps) => pat.fmap((i) => haps[i % haps.length]));
};
@@ -990,9 +1016,6 @@ function _composeOp(a, b, func) {
/**
* Applies the given structure to the pattern:
*
* @name struct
* @memberof Pattern
* @returns Pattern
* @example
* note("c3,eb3,g3")
* .struct("x ~ x ~ ~ x ~ x ~ ~ ~ x ~ x ~ ~")
@@ -1004,18 +1027,37 @@ function _composeOp(a, b, func) {
Pattern.prototype.structAll = function (...args) {
return this.keep.out(...args);
};
/**
* Returns silence when mask is 0 or "~"
*
* @example
* note("c [eb,g] d [eb,g]").mask("<1 [0 1]>").slow(2)
*/
Pattern.prototype.mask = function (...args) {
return this.keepif.in(...args);
};
Pattern.prototype.maskAll = function (...args) {
return this.keep.in(...args);
};
/**
* Resets the pattern to the start of the cycle for each onset of the reset pattern.
*
* @example
* s("<bd lt> sd, hh*4").reset("<x@3 x(3,8)>")
*/
Pattern.prototype.reset = function (...args) {
return this.keepif.trig(...args);
};
Pattern.prototype.resetAll = function (...args) {
return this.keep.trig(...args);
};
/**
* Restarts the pattern for each onset of the restart pattern.
* While reset will only reset the current cycle, restart will start from cycle 0.
*
* @example
* s("<bd lt> sd, hh*4").restart("<x@3 x(3,8)>")
*/
Pattern.prototype.restart = function (...args) {
return this.keepif.trigzero(...args);
};
@@ -1029,6 +1071,7 @@ export const polyrhythm = stack;
export const pr = stack;
// methods that create patterns, which are added to patternified Pattern methods
// TODO: remove? this is only used in old transpiler (shapeshifter)
Pattern.prototype.factories = {
pure,
stack,
@@ -1098,6 +1141,7 @@ export function reify(thing) {
/** The given items are played at the same time at the same length.
*
* @return {Pattern}
* @synonyms polyrhythm, pr
* @example
* stack(g3, b3, [e4, d4]).note() // "g3,b3,[e4,d4]".note()
*/
@@ -1152,24 +1196,10 @@ export function slowcatPrime(...pats) {
return new Pattern(query).splitQueries();
}
/** Concatenation: as with {@link slowcat}, but squashes a cycle from each pattern into one cycle
*
* Synonyms: {@link Pattern.seq}, {@link Pattern.sequence}
*
* @param {...any} items - The items to concatenate
* @return {Pattern}
* @example
* fastcat(e5, b4, [d5, c5])
* // sequence(e5, b4, [d5, c5])
* // seq(e5, b4, [d5, c5])
*/
export function fastcat(...pats) {
return slowcat(...pats)._fast(pats.length);
}
/** The given items are con**cat**enated, where each one takes one cycle. Synonym: slowcat
/** The given items are con**cat**enated, where each one takes one cycle.
*
* @param {...any} items - The items to concatenate
* @synonyms slowcat
* @return {Pattern}
* @example
* cat(e5, b4, [d5, c5]).note() // "<e5 b4 [d5 c5]>".note()
@@ -1196,12 +1226,17 @@ export function timeCat(...timepats) {
return stack(...pats);
}
export function fastcat(...pats) {
return slowcat(...pats)._fast(pats.length);
}
/** See {@link fastcat} */
export function sequence(...pats) {
return fastcat(...pats);
}
/** Like **cat**, but the items are crammed into one cycle. Synonyms: fastcat, sequence
/** Like **cat**, but the items are crammed into one cycle.
* @synonyms fastcat, sequence
* @example
* seq(e5, b4, [d5, c5]).note() // "e5 b4 [d5 c5]".note()
*
@@ -1257,7 +1292,7 @@ export function polymeterSteps(steps, ...args) {
/**
* Combines the given lists of patterns with the same pulse. This will create so called polymeters when different sized sequences are used.
* @name polymeter
* @synonyms pm
* @example
* polymeter(["c", "eb", "g"], ["c2", "g2"]).note()
* // "{c eb g, c2 g2}".note()
@@ -1303,6 +1338,13 @@ export const and = curry((a, b) => reify(b).and(a));
export const or = curry((a, b) => reify(b).or(a));
export const func = curry((a, b) => reify(b).func(a));
/**
* Registers a new pattern method. The method is added to the Pattern class + the standalone function is returned from register.
*
* @param {string} name name of the function
* @param {function} func function with 1 or more params, where last is the current pattern
*
*/
export function register(name, func) {
if (Array.isArray(name)) {
const result = {};
@@ -1435,23 +1477,27 @@ export const range = register('range', function (min, max, pat) {
});
/**
* Assumes a numerical pattern, containing unipolar values in the range 0 ..
* 1. Returns a new pattern with values scaled to the given min/max range,
* Assumes a numerical pattern, containing unipolar values in the range 0 .. 1
* Returns a new pattern with values scaled to the given min/max range,
* following an exponential curve.
* @param {Number} min
* @param {Number} max
* @name rangex
* @memberof Pattern
* @returns Pattern
* @example
* s("bd sd,hh*4").cutoff(sine.rangex(500,2000).slow(4))
*/
export const rangex = register('rangex', function (min, max, pat) {
return pat._range(Math.log(min), Math.log(max)).fmap(Math.exp);
});
/**
* Assumes a numerical pattern, containing bipolar values in the range -1 ..
* 1. Returns a new pattern with values scaled to the given min/max range.
* @param {Number} min
* @param {Number} max
* Assumes a numerical pattern, containing bipolar values in the range -1 .. 1
* Returns a new pattern with values scaled to the given min/max range.
* @name range2
* @memberof Pattern
* @returns Pattern
* @example
* s("bd sd,hh*4").cutoff(sine2.range2(500,2000).slow(4))
*/
export const range2 = register('range2', function (min, max, pat) {
return pat.fromBipolar()._range(min, max);
@@ -1460,8 +1506,16 @@ export const range2 = register('range2', function (min, max, pat) {
//////////////////////////////////////////////////////////////////////
// Structural and temporal transformations
// Compress each cycle into the given timespan, leaving a gap
/** Compress each cycle into the given timespan, leaving a gap
* @example
* cat(
* s("bd sd").compress(.25,.75),
* s("~ bd sd ~")
* )
*/
export const compress = register('compress', function (b, e, pat) {
b = Fraction(b);
e = Fraction(e);
if (b.gt(e) || b.gt(1) || e.gt(1) || b.lt(0) || e.lt(0)) {
return silence;
}
@@ -1472,6 +1526,13 @@ export const { compressSpan, compressspan } = register(['compressSpan', 'compres
return pat._compress(span.begin, span.end);
});
/**
* speeds up a pattern like fast, but rather than it playing multiple times as fast would it instead leaves a gap in the remaining space of the cycle. For example, the following will play the sound pattern "bd sn" only once but compressed into the first half of the cycle, i.e. twice as fast.
* @name fastGap
* @synonyms fastgap
* @example
* s("bd sd").fastGap(2)
*/
export const { fastGap, fastgap } = register(['fastGap', 'fastgap'], function (factor, pat) {
// A bit fiddly, to drop zero-width queries at the start of the next cycle
const qf = function (span) {
@@ -1502,10 +1563,14 @@ export const { fastGap, fastgap } = register(['fastGap', 'fastgap'], function (f
return pat.withQuerySpanMaybe(qf).withHap(ef).splitQueries();
});
// Similar to compress, but doesn't leave gaps, and the 'focus' can be
// bigger than a cycle
/**
* Similar to compress, but doesn't leave gaps, and the 'focus' can be bigger than a cycle
* @example
* s("bd hh sd hh").focus(1/4, 3/4)
*/
export const focus = register('focus', function (b, e, pat) {
b = Fraction(b);
e = Fraction(e);
return pat._fast(Fraction(1).div(e.sub(b))).late(b.cyclePos());
});
@@ -1513,6 +1578,10 @@ export const { focusSpan, focusspan } = register(['focusSpan', 'focusspan'], fun
return pat._focus(span.begin, span.end);
});
/** The ply function repeats each event the given number of times.
* @example
* s("bd ~ sd cp").ply("<1 2 3>")
*/
export const ply = register('ply', function (factor, pat) {
return pat.fmap((x) => pure(x)._fast(factor)).squeezeJoin();
});
@@ -1521,6 +1590,7 @@ export const ply = register('ply', function (factor, pat) {
* Speed up a pattern by the given factor. Used by "*" in mini notation.
*
* @name fast
* @synonyms density
* @memberof Pattern
* @param {number | Pattern} factor speed up factor
* @returns Pattern
@@ -1533,10 +1603,20 @@ export const { fast, density } = register(['fast', 'density'], function (factor,
return fastQuery.withHapTime((t) => t.div(factor));
});
/**
* Both speeds up the pattern (like 'fast') and the sample playback (like 'speed').
* @example
* s("bd sd:2").hurry("<1 2 4 3>").slow(1.5)
*/
export const hurry = register('hurry', function (r, pat) {
return pat._fast(r).mul(pure({ speed: r }));
});
/**
* Slow down a pattern over the given number of cycles. Like the "/" operator in mini notation.
*
* @name slow
* @synonyms sparsity
* @memberof Pattern
* @param {number | Pattern} factor slow down factor
* @returns Pattern
@@ -1547,12 +1627,22 @@ export const { slow, sparsity } = register(['slow', 'sparsity'], function (facto
return pat._fast(Fraction(1).div(factor));
});
// Should these really be in alphabetical order? a shame to split
// fast/slow, inside/outside..
/**
* Carries out an operation 'inside' a cycle.
* @example
* "0 1 2 3 4 3 2 1".inside(4, rev).scale('C major').note()
* // "0 1 2 3 4 3 2 1".slow(4).rev().fast(4).scale('C major').note()
*/
export const inside = register('inside', function (factor, f, pat) {
return f(pat._slow(factor))._fast(factor);
});
/**
* Carries out an operation 'outside' a cycle.
* @example
* "<[0 1] 2 [3 4] 5>".outside(4, rev).scale('C major').note()
* // "<[0 1] 2 [3 4] 5>".fast(4).rev().slow(4).scale('C major').note()
*/
export const outside = register('outside', function (factor, f, pat) {
return f(pat._fast(factor))._slow(factor);
});
@@ -1607,11 +1697,16 @@ export const { firstOf, every } = register(['firstOf', 'every'], function (n, fu
* @example
* "<c3 eb3 g3>".scale('C minor').apply(scaleTranspose("0,2,4")).note()
*/
// TODO: remove or dedupe with layer?
export const apply = register('apply', function (func, pat) {
return func(pat);
});
// cpm = cycles per minute
/**
* Plays the pattern at the given cycles per minute.
* @example
* s("<bd sd>,hh*2").cpm(90) // = 90 bpm
*/
// TODO - global clock
export const cpm = register('cpm', function (cpm, pat) {
return pat._fast(cpm / 60);
@@ -1647,6 +1742,13 @@ export const late = register('late', function (offset, pat) {
return pat._early(Fraction(0).sub(offset));
});
/**
* Plays a portion of a pattern, specified by the beginning and end of a time span. The new resulting pattern is played over the time period of the original pattern:
*
* @example
* s("bd*2 hh*3 [sd bd]*2 perc").zoom(0.25, 0.75)
* // s("hh*3 [sd bd]*2") // equivalent
*/
export const zoom = register('zoom', function (s, e, pat) {
e = Fraction(e);
s = Fraction(s);
@@ -1661,6 +1763,12 @@ export const { zoomArc, zoomarc } = register(['zoomArc', 'zoomarc'], function (a
return pat.zoom(a.begin, a.end);
});
/**
* Selects the given fraction of the pattern and repeats that part to fill the remainder of the cycle.
* @param {number} fraction fraction to select
* @example
* s("lt ht mt cp, [hh oh]*2").linger("<1 .5 .25 .125>")
*/
export const linger = register('linger', function (t, pat) {
if (t == 0) {
return silence;
@@ -1670,10 +1778,23 @@ export const linger = register('linger', function (t, pat) {
return pat._zoom(0, t)._slow(t);
});
/**
* Samples the pattern at a rate of n events per cycle. Useful for turning a continuous pattern into a discrete one.
* @param {number} segments number of segments per cycle
* @example
* note(saw.range(0,12).segment(24)).add(40)
*/
export const segment = register('segment', function (rate, pat) {
return pat.struct(pure(true)._fast(rate));
});
/**
* Swaps 1s and 0s in a binary pattern.
* @name invert
* @synonyms inv
* @example
* s("bd").struct("1 0 0 1 0 0 1 0".lastOf(4, invert))
*/
export const { invert, inv } = register(['invert', 'inv'], function (pat) {
// Swap true/false in a binary pattern
return pat.fmap((x) => !x);
@@ -1745,14 +1866,57 @@ export const rev = register('rev', function (pat) {
return new Pattern(query).splitQueries();
});
/** Like press, but allows you to specify the amount by which each
* event is shifted. pressBy(0.5) is the same as press, while
* pressBy(1/3) shifts each event by a third of its timespan.
* @example
* stack(s("hh*4"),
* s("bd mt sd ht").pressBy("<0 0.5 0.25>")
* ).slow(2)
*/
export const pressBy = register('pressBy', function (r, pat) {
return pat.fmap((x) => pure(x).compress(r, 1)).squeezeJoin();
});
/**
* Syncopates a rhythm, by shifting each event halfway into its timespan.
* @example
* stack(s("hh*4"),
* s("bd mt sd ht").every(4, press)
* ).slow(2)
*/
export const press = register('press', function (pat) {
return pat._pressBy(0.5);
});
/**
* Silences a pattern.
* @example
* stack(
* s("bd").hush(),
* s("hh*3")
* )
*/
export const hush = register('hush', function (pat) {
return silence;
});
/**
* Applies `rev` to a pattern every other cycle, so that the pattern alternates between forwards and backwards.
* @example
* note("c d e g").palindrome()
*/
export const palindrome = register('palindrome', function (pat) {
return pat.every(2, rev);
});
/**
* Jux with adjustable stereo width. 0 = mono, 1 = full stereo.
* @name juxBy
* @synonyms juxby
* @example
* s("lt ht mt ht hh").juxBy("<0 .5 1>/2", rev)
*/
export const { juxBy, juxby } = register(['juxBy', 'juxby'], function (by, func, pat) {
by /= 2;
const elem_or = function (dict, key, dflt) {
@@ -1767,23 +1931,19 @@ export const { juxBy, juxby } = register(['juxBy', 'juxby'], function (by, func,
return stack(left, func(right));
});
/**
* The jux function creates strange stereo effects, by applying a function to a pattern, but only in the right-hand channel.
* @example
* s("lt ht mt ht hh").jux(rev)
*/
export const jux = register('jux', function (func, pat) {
return pat._juxBy(1, func, pat);
});
export const { stutWith, stutwith } = register(['stutWith', 'stutwith'], function (times, time, func, pat) {
return stack(...listRange(0, times - 1).map((i) => func(pat.late(Fraction(time).mul(i)), i)));
});
export const stut = register('stut', function (times, feedback, time, pat) {
return pat._stutWith(times, time, (pat, i) => pat.velocity(Math.pow(feedback, i)));
});
/**
* Superimpose and offset multiple times, applying the given function each time.
* @name echoWith
* @memberof Pattern
* @returns Pattern
* @synonyms echowith, stutWith, stutwith
* @param {number} times how many times to repeat
* @param {number} time cycle offset between iterations
* @param {function} func function to apply, given the pattern and the iteration index
@@ -1792,9 +1952,12 @@ export const stut = register('stut', function (times, feedback, time, pat) {
* .echoWith(4, 1/8, (p,n) => p.add(n*2))
* .scale('C minor').note().legato(.2)
*/
export const { echoWith, echowith } = register(['echoWith', 'echowith'], function (times, time, func, pat) {
return stack(...listRange(0, times - 1).map((i) => func(pat.late(Fraction(time).mul(i)), i)));
});
export const { echoWith, echowith, stutWith, stutwith } = register(
['echoWith', 'echowith', 'stutWith', 'stutwith'],
function (times, time, func, pat) {
return stack(...listRange(0, times - 1).map((i) => func(pat.late(Fraction(time).mul(i)), i)));
},
);
/**
* Superimpose and offset multiple times, gradually decreasing the velocity
@@ -1811,6 +1974,19 @@ export const echo = register('echo', function (times, time, feedback, pat) {
return pat._echoWith(times, time, (pat, i) => pat.velocity(Math.pow(feedback, i)));
});
/**
* Deprecated. Like echo, but the last 2 parameters are flipped.
* @name stut
* @param {number} times how many times to repeat
* @param {number} feedback velocity multiplicator for each iteration
* @param {number} time cycle offset between iterations
* @example
* s("bd sd").stut(3, .8, 1/6)
*/
export const stut = register('stut', function (times, feedback, time, pat) {
return pat._echoWith(times, time, (pat, i) => pat.velocity(Math.pow(feedback, i)));
});
/**
* Divides a pattern into a given number of subdivisions, plays the subdivisions in order, but increments the starting subdivision each cycle. The pattern wraps to the first subdivision after the last subdivision is played.
* @name iter
@@ -1836,6 +2012,7 @@ export const iter = register('iter', function (times, pat) {
/**
* Like `iter`, but plays the subdivisions in reverse order. Known as iter' in tidalcycles
* @name iterBack
* @synonyms iterback
* @memberof Pattern
* @returns Pattern
* @example
@@ -1867,6 +2044,7 @@ export const chunk = register('chunk', function (n, func, pat) {
/**
* Like `chunk`, but cycles through the parts in reverse order. Known as chunk' in tidalcycles
* @name chunkBack
* @synonyms chunkback
* @memberof Pattern
* @returns Pattern
* @example
@@ -1879,16 +2057,29 @@ export const { chunkBack, chunkback } = register(['chunkBack', 'chunkback'], fun
// TODO - redefine elsewhere in terms of mask
export const bypass = register('bypass', function (on, pat) {
on = Boolean(parseInt(on));
return on ? silence : this;
return on ? silence : pat;
});
/**
* Loops the pattern inside at `offset` for `cycles`.
* @param {number} offset start point of loop in cycles
* @param {number} cycles loop length in cycles
* @example
* // Looping a portion of randomness
* note(irand(8).segment(4).scale('C3 minor')).ribbon(1337, 2)
*/
export const ribbon = register('ribbon', (offset, cycles, pat) => pat.early(offset).restart(pure(1).slow(cycles)));
// sets absolute duration of haps
// TODO - fix
export const duration = register('duration', function (value, pat) {
return pat.withHapSpan((span) => new TimeSpan(span.begin, span.begin.add(value)));
});
// TODO - make control?
/**
* Sets the color of the hap in visualizations like pianoroll or highlighting.
*/
// TODO: move this to controls https://github.com/tidalcycles/strudel/issues/288
export const { color, colour } = register(['color', 'colour'], function (color, pat) {
return pat.withContext((context) => ({ ...context, color }));
});
@@ -1916,6 +2107,7 @@ export const velocity = register('velocity', function (velocity, pat) {
*/
// TODO - fix
export const legato = register('legato', function (value, pat) {
value = Fraction(value);
return pat.withHapSpan((span) => new TimeSpan(span.begin, span.begin.add(span.end.sub(span.begin).mul(value))));
});
+12
View File
@@ -283,3 +283,15 @@ export function pianoroll({
ctx.stroke();
return this;
}
function getOptions(drawTime, options = {}) {
let [lookbehind, lookahead] = drawTime;
lookbehind = Math.abs(lookbehind);
const cycles = lookahead + lookbehind;
const playhead = lookbehind / cycles;
return { fold: 1, ...options, cycles, playhead };
}
Pattern.prototype.punchcard = function (options) {
return this.onPaint((ctx, time, haps, drawTime) => pianoroll({ ctx, time, haps, ...getOptions(drawTime, options) }));
};
+2 -2
View File
@@ -12,8 +12,8 @@ export function repl({
afterEval,
getTime,
transpiler,
editPattern,
onToggle,
editPattern,
}) {
const scheduler = new Cyclist({
interval,
@@ -35,7 +35,7 @@ export function repl({
getTime,
onToggle,
});
setTime(() => scheduler.getPhase()); // TODO: refactor?
setTime(() => scheduler.now()); // TODO: refactor?
const evaluate = async (code, autostart = true) => {
if (!code) {
throw new Error('no code to evaluate');
+8
View File
@@ -114,6 +114,14 @@ const timeToRands = (t, n) => timeToRandsPrime(timeToIntSeed(t), n);
*
*/
/**
* A discrete pattern of numbers from 0 to n-1
* @example
* run(4).scale('C4 major').note()
* // "0 1 2 3".scale('C4 major').note()
*/
export const run = (n) => saw.range(0, n).floor().segment(n);
/**
* A continuous pattern of random numbers, between 0 and 1.
*
+23
View File
@@ -44,6 +44,7 @@ import {
ply,
rev,
time,
run,
} from '../index.mjs';
import { steady } from '../signal.mjs';
@@ -908,6 +909,18 @@ describe('Pattern', () => {
);
});
});
describe('run', () => {
it('Can run', () => {
expect(run(4).firstCycle()).toStrictEqual(sequence(0, 1, 2, 3).firstCycle());
});
});
describe('ribbon', () => {
it('Can ribbon', () => {
expect(cat(0, 1, 2, 3, 4, 5, 6, 7).ribbon(2, 4).fast(4).firstCycle()).toStrictEqual(
sequence(2, 3, 4, 5).firstCycle(),
);
});
});
describe('linger', () => {
it('Can linger on the first quarter of a cycle', () => {
expect(sequence(0, 1, 2, 3, 4, 5, 6, 7).linger(0.25).firstCycle()).toStrictEqual(
@@ -936,4 +949,14 @@ describe('Pattern', () => {
expect(stack(sequence('a', silence), pure('a').mask(0, 1)).defragmentHaps().firstCycle().length).toStrictEqual(2);
});
});
describe('press', () => {
it('Can syncopate events', () => {
sameFirst(sequence('a', 'b', 'c', 'd').press(), sequence(silence, 'a', silence, 'b', silence, 'c', silence, 'd'));
});
});
describe('hurry', () => {
it('Can speed up patterns and sounds', () => {
sameFirst(s('a', 'b').hurry(2), s('a', 'b').fast(2).speed(2));
});
});
});
+1 -1
View File
@@ -26,7 +26,7 @@ export const backgroundImage = function (src, animateOptions = {}) {
({
style: () => (container.style = bg + ';' + value),
className: () => (container.className = value + ' ' + initialClassName),
}[option]());
})[option]();
};
const funcOptions = Object.entries(animateOptions).filter(([_, v]) => typeof v === 'function');
const stringOptions = Object.entries(animateOptions).filter(([_, v]) => typeof v === 'string');
+19
View File
@@ -0,0 +1,19 @@
import { defineConfig } from 'vite';
import { dependencies } from './package.json';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [],
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
},
rollupOptions: {
external: [...Object.keys(dependencies)],
},
target: 'esnext',
},
});
@@ -92,6 +92,7 @@ async function load() {
['message'].forEach((k) => _csound.on(k, (...args) => eventLogger(k, args)));
await _csound.setOption('-m0d'); // see -m flag https://csound.com/docs/manual/CommandFlags.html
await _csound.setOption('--sample-accurate');
await _csound.setOption('-odac');
await _csound.compileCsdText(csd);
// await _csound.compileOrc(livecodeOrc);
await _csound.compileOrc(presetsOrc);
+14 -4
View File
@@ -1,10 +1,15 @@
{
"name": "@strudel.cycles/csound",
"version": "0.5.1",
"version": "0.6.2",
"description": "csound bindings for strudel",
"main": "csound.mjs",
"main": "index.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": {
"test": "echo \"No tests present.\" && exit 0"
"build": "vite build",
"prepublishOnly": "npm run build"
},
"repository": {
"type": "git",
@@ -27,6 +32,11 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@csound/browser": "^6.18.3"
"@csound/browser": "6.18.5",
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*"
},
"devDependencies": {
"vite": "^3.2.2"
}
}
+19
View File
@@ -0,0 +1,19 @@
import { defineConfig } from 'vite';
import { dependencies } from './package.json';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [],
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
},
rollupOptions: {
external: [...Object.keys(dependencies)],
},
target: 'esnext',
},
});
+4
View File
@@ -3,6 +3,10 @@
This package contains the strudel code transformer and evaluator.
It allows creating strudel patterns from input code that is optimized for minimal keystrokes and human readability.
## Deprecation Note
This package will not be developed further. Consider using `@strudel.cycles/transpiler` as a replacement.
## Install
```sh
-297
View File
@@ -1,297 +0,0 @@
{
"name": "@strudel.cycles/eval",
"version": "0.5.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@strudel.cycles/eval",
"version": "0.1.1",
"license": "AGPL-3.0-or-later",
"dependencies": {
"estraverse": "^5.3.0",
"shift-ast": "^6.1.0",
"shift-codegen": "^7.0.3",
"shift-parser": "^7.0.3",
"shift-spec": "^2018.0.2",
"shift-traverser": "^1.0.0"
}
},
"node_modules/estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"engines": {
"node": ">=4.0"
}
},
"node_modules/esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/multimap": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/multimap/-/multimap-1.1.0.tgz",
"integrity": "sha512-0ZIR9PasPxGXmRsEF8jsDzndzHDj7tIav+JUmvIFB/WHswliFnquxECT/De7GR4yg99ky/NlRKJT82G1y271bw=="
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/shift-ast": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.1.0.tgz",
"integrity": "sha512-Vj4XUIJIFPIh6VcBGJ1hjH/kM88XGer94Pr7Rvxa+idEylDsrwtLw268HoxGo5xReL6T3DdRl/9/Pr1XihZ/8Q=="
},
"node_modules/shift-codegen": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/shift-codegen/-/shift-codegen-7.0.3.tgz",
"integrity": "sha512-dfCVVdBF0qZ6pkajQ3bjxRdNEltyxEITVe7tBJkQt2eCI3znUkSxq0VSe/tTWq1LKHeAS4HuOiqYEuHMFkSq9w==",
"dependencies": {
"esutils": "^2.0.2",
"object-assign": "^4.1.0",
"shift-reducer": "6.0.0"
}
},
"node_modules/shift-parser": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/shift-parser/-/shift-parser-7.0.3.tgz",
"integrity": "sha512-uYX2ORyZfKZrUc4iKKkO9KOhzUSxCrSBk7QK6ZmShId+BOo1gh1IwecVy97ynyOTpmhPWUttjC8BzsnQl65Zew==",
"dependencies": {
"multimap": "^1.0.2",
"shift-ast": "6.0.0",
"shift-reducer": "6.0.0",
"shift-regexp-acceptor": "2.0.3"
}
},
"node_modules/shift-parser/node_modules/shift-ast": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.0.0.tgz",
"integrity": "sha512-XXxDcEBWVBzqWXfNYJlLyJ1/9kMvOXVRXiqPjkOrTCC5qRsBvEMJMRLLFhU3tn8ue56Y7IZyBE6bexFum5QLUw=="
},
"node_modules/shift-reducer": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/shift-reducer/-/shift-reducer-6.0.0.tgz",
"integrity": "sha512-2rJraRP8drIOjvaE/sALa+0tGJmMVUzlmS3wIJerJbaYuCjpFAiF0WjkTOFVtz1144Nm/ECmqeG+7yRhuMVsMg==",
"dependencies": {
"shift-ast": "6.0.0"
}
},
"node_modules/shift-reducer/node_modules/shift-ast": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.0.0.tgz",
"integrity": "sha512-XXxDcEBWVBzqWXfNYJlLyJ1/9kMvOXVRXiqPjkOrTCC5qRsBvEMJMRLLFhU3tn8ue56Y7IZyBE6bexFum5QLUw=="
},
"node_modules/shift-regexp-acceptor": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/shift-regexp-acceptor/-/shift-regexp-acceptor-2.0.3.tgz",
"integrity": "sha512-sxL7e5JNUFxm+gutFRXktX2D6KVgDAHNuDsk5XHB9Z+N5yXooZG6pdZ1GEbo3Jz6lF7ETYLBC4WAjIFm2RKTmA==",
"dependencies": {
"unicode-match-property-ecmascript": "1.0.4",
"unicode-match-property-value-ecmascript": "1.0.2",
"unicode-property-aliases-ecmascript": "1.0.4"
}
},
"node_modules/shift-spec": {
"version": "2018.0.2",
"resolved": "https://registry.npmjs.org/shift-spec/-/shift-spec-2018.0.2.tgz",
"integrity": "sha512-5CP/cKDEim4rZ6ViCSipTLY2U7HJr8q/kpDuCBmebFqbx/0DeozWO+9ienHmYjgGLDfHrqj+LBAN67FRK2vE6w=="
},
"node_modules/shift-traverser": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/shift-traverser/-/shift-traverser-1.0.0.tgz",
"integrity": "sha512-DMY3512wJbdC+IC+nhLH3/Stgr2BbxbNcg7qyZ6+e5qNnNs8TBQJWdMsRgHlX1JXwF4C0ONKS8VUxsPT0Tf7aw==",
"dependencies": {
"estraverse": "4.2.0",
"shift-spec": "2018.0.0"
}
},
"node_modules/shift-traverser/node_modules/estraverse": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
"integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/shift-traverser/node_modules/shift-spec": {
"version": "2018.0.0",
"resolved": "https://registry.npmjs.org/shift-spec/-/shift-spec-2018.0.0.tgz",
"integrity": "sha512-/aiPOkj7dbe+CV2VZhIMTHQToZmgniofpRG7Yr7x2/0sO6CSVC++py1Wzf+s+rWSTDHKcLvziVAxjRRV4i4EoQ=="
},
"node_modules/unicode-canonical-property-names-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
"integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==",
"engines": {
"node": ">=4"
}
},
"node_modules/unicode-match-property-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz",
"integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==",
"dependencies": {
"unicode-canonical-property-names-ecmascript": "^1.0.4",
"unicode-property-aliases-ecmascript": "^1.0.4"
},
"engines": {
"node": ">=4"
}
},
"node_modules/unicode-match-property-value-ecmascript": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.2.tgz",
"integrity": "sha512-Rx7yODZC1L/T8XKo/2kNzVAQaRE88AaMvI1EF/Xnj3GW2wzN6fop9DDWuFAKUVFH7vozkz26DzP0qyWLKLIVPQ==",
"engines": {
"node": ">=4"
}
},
"node_modules/unicode-property-aliases-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz",
"integrity": "sha512-2WSLa6OdYd2ng8oqiGIWnJqyFArvhn+5vgx5GTxMbUYjCYKUcuKS62YLFF0R/BDGlB1yzXjQOLtPAfHsgirEpg==",
"engines": {
"node": ">=4"
}
}
},
"dependencies": {
"estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="
},
"esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
},
"multimap": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/multimap/-/multimap-1.1.0.tgz",
"integrity": "sha512-0ZIR9PasPxGXmRsEF8jsDzndzHDj7tIav+JUmvIFB/WHswliFnquxECT/De7GR4yg99ky/NlRKJT82G1y271bw=="
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"shift-ast": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.1.0.tgz",
"integrity": "sha512-Vj4XUIJIFPIh6VcBGJ1hjH/kM88XGer94Pr7Rvxa+idEylDsrwtLw268HoxGo5xReL6T3DdRl/9/Pr1XihZ/8Q=="
},
"shift-codegen": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/shift-codegen/-/shift-codegen-7.0.3.tgz",
"integrity": "sha512-dfCVVdBF0qZ6pkajQ3bjxRdNEltyxEITVe7tBJkQt2eCI3znUkSxq0VSe/tTWq1LKHeAS4HuOiqYEuHMFkSq9w==",
"requires": {
"esutils": "^2.0.2",
"object-assign": "^4.1.0",
"shift-reducer": "6.0.0"
}
},
"shift-parser": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/shift-parser/-/shift-parser-7.0.3.tgz",
"integrity": "sha512-uYX2ORyZfKZrUc4iKKkO9KOhzUSxCrSBk7QK6ZmShId+BOo1gh1IwecVy97ynyOTpmhPWUttjC8BzsnQl65Zew==",
"requires": {
"multimap": "^1.0.2",
"shift-ast": "6.0.0",
"shift-reducer": "6.0.0",
"shift-regexp-acceptor": "2.0.3"
},
"dependencies": {
"shift-ast": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.0.0.tgz",
"integrity": "sha512-XXxDcEBWVBzqWXfNYJlLyJ1/9kMvOXVRXiqPjkOrTCC5qRsBvEMJMRLLFhU3tn8ue56Y7IZyBE6bexFum5QLUw=="
}
}
},
"shift-reducer": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/shift-reducer/-/shift-reducer-6.0.0.tgz",
"integrity": "sha512-2rJraRP8drIOjvaE/sALa+0tGJmMVUzlmS3wIJerJbaYuCjpFAiF0WjkTOFVtz1144Nm/ECmqeG+7yRhuMVsMg==",
"requires": {
"shift-ast": "6.0.0"
},
"dependencies": {
"shift-ast": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.0.0.tgz",
"integrity": "sha512-XXxDcEBWVBzqWXfNYJlLyJ1/9kMvOXVRXiqPjkOrTCC5qRsBvEMJMRLLFhU3tn8ue56Y7IZyBE6bexFum5QLUw=="
}
}
},
"shift-regexp-acceptor": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/shift-regexp-acceptor/-/shift-regexp-acceptor-2.0.3.tgz",
"integrity": "sha512-sxL7e5JNUFxm+gutFRXktX2D6KVgDAHNuDsk5XHB9Z+N5yXooZG6pdZ1GEbo3Jz6lF7ETYLBC4WAjIFm2RKTmA==",
"requires": {
"unicode-match-property-ecmascript": "1.0.4",
"unicode-match-property-value-ecmascript": "1.0.2",
"unicode-property-aliases-ecmascript": "1.0.4"
}
},
"shift-spec": {
"version": "2018.0.2",
"resolved": "https://registry.npmjs.org/shift-spec/-/shift-spec-2018.0.2.tgz",
"integrity": "sha512-5CP/cKDEim4rZ6ViCSipTLY2U7HJr8q/kpDuCBmebFqbx/0DeozWO+9ienHmYjgGLDfHrqj+LBAN67FRK2vE6w=="
},
"shift-traverser": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/shift-traverser/-/shift-traverser-1.0.0.tgz",
"integrity": "sha512-DMY3512wJbdC+IC+nhLH3/Stgr2BbxbNcg7qyZ6+e5qNnNs8TBQJWdMsRgHlX1JXwF4C0ONKS8VUxsPT0Tf7aw==",
"requires": {
"estraverse": "4.2.0",
"shift-spec": "2018.0.0"
},
"dependencies": {
"estraverse": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
"integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM="
},
"shift-spec": {
"version": "2018.0.0",
"resolved": "https://registry.npmjs.org/shift-spec/-/shift-spec-2018.0.0.tgz",
"integrity": "sha512-/aiPOkj7dbe+CV2VZhIMTHQToZmgniofpRG7Yr7x2/0sO6CSVC++py1Wzf+s+rWSTDHKcLvziVAxjRRV4i4EoQ=="
}
}
},
"unicode-canonical-property-names-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
"integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ=="
},
"unicode-match-property-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz",
"integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==",
"requires": {
"unicode-canonical-property-names-ecmascript": "^1.0.4",
"unicode-property-aliases-ecmascript": "^1.0.4"
}
},
"unicode-match-property-value-ecmascript": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.2.tgz",
"integrity": "sha512-Rx7yODZC1L/T8XKo/2kNzVAQaRE88AaMvI1EF/Xnj3GW2wzN6fop9DDWuFAKUVFH7vozkz26DzP0qyWLKLIVPQ=="
},
"unicode-property-aliases-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz",
"integrity": "sha512-2WSLa6OdYd2ng8oqiGIWnJqyFArvhn+5vgx5GTxMbUYjCYKUcuKS62YLFF0R/BDGlB1yzXjQOLtPAfHsgirEpg=="
}
}
}
+16 -5
View File
@@ -1,15 +1,21 @@
{
"name": "@strudel.cycles/eval",
"version": "0.5.0",
"version": "0.6.2",
"description": "Code evaluator for strudel",
"main": "index.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": {
"build": "vite build",
"test": "vitest run",
"prepublishOnly": "npm run build"
},
"type": "module",
"directories": {
"test": "test"
},
"scripts": {
"test": "vitest run"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tidalcycles/strudel.git"
@@ -28,12 +34,17 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.5.0",
"@strudel.cycles/core": "workspace:*",
"estraverse": "^5.3.0",
"shift-ast": "^7.0.0",
"shift-codegen": "^8.1.0",
"shift-parser": "^8.0.0",
"shift-spec": "^2019.0.0",
"shift-traverser": "^1.0.0"
},
"devDependencies": {
"@strudel.cycles/mini": "workspace:*",
"vite": "^3.2.2",
"vitest": "^0.25.7"
}
}
+19
View File
@@ -0,0 +1,19 @@
import { defineConfig } from 'vite';
import { dependencies } from './package.json';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [],
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
},
rollupOptions: {
external: [...Object.keys(dependencies)],
},
target: 'esnext',
},
});
-4
View File
@@ -7,7 +7,3 @@ This package adds midi functionality to strudel Patterns.
```sh
npm i @strudel.cycles/midi --save
```
## Dev Notes
- is this package really necessary? currently, /tone also depends on webmidi through @tonejs/piano. Either move piano out of /tone or merge /midi into /tone...
-130
View File
@@ -1,130 +0,0 @@
{
"name": "@strudel.cycles/midi",
"version": "0.5.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@strudel.cycles/midi",
"version": "0.1.1",
"license": "AGPL-3.0-or-later",
"dependencies": {
"tone": "^14.7.77",
"webmidi": "^2.5.2"
}
},
"node_modules/@babel/runtime": {
"version": "7.17.8",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.8.tgz",
"integrity": "sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==",
"dependencies": {
"regenerator-runtime": "^0.13.4"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/automation-events": {
"version": "4.0.14",
"resolved": "https://registry.npmjs.org/automation-events/-/automation-events-4.0.14.tgz",
"integrity": "sha512-CB2Me0yW8sz7gSGwMiSfgfs1Oqlgs53k+eVESN6axvRyMAD3zlSp2nqndD2TQAtW3yOtSEJWNGsw0r48+f1wtw==",
"dependencies": {
"@babel/runtime": "^7.17.2",
"tslib": "^2.3.1"
},
"engines": {
"node": ">=12.20.1"
}
},
"node_modules/regenerator-runtime": {
"version": "0.13.9",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
},
"node_modules/standardized-audio-context": {
"version": "25.3.21",
"resolved": "https://registry.npmjs.org/standardized-audio-context/-/standardized-audio-context-25.3.21.tgz",
"integrity": "sha512-CZEnayJJjNefeU+z1QGDhaid1LAAYxWYa2ipNk75ropwec9rq6fmclyhXb1wGtDsZ402irX3HLt1U/PwP9+1fA==",
"dependencies": {
"@babel/runtime": "^7.17.2",
"automation-events": "^4.0.14",
"tslib": "^2.3.1"
}
},
"node_modules/tone": {
"version": "14.7.77",
"resolved": "https://registry.npmjs.org/tone/-/tone-14.7.77.tgz",
"integrity": "sha512-tCfK73IkLHyzoKUvGq47gyDyxiKLFvKiVCOobynGgBB9Dl0NkxTM2p+eRJXyCYrjJwy9Y0XCMqD3uOYsYt2Fdg==",
"dependencies": {
"standardized-audio-context": "^25.1.8",
"tslib": "^2.0.1"
}
},
"node_modules/tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"node_modules/webmidi": {
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/webmidi/-/webmidi-2.5.3.tgz",
"integrity": "sha512-PyMGvKcDGpvbQUfnmBORQJciyG3VAZ4aHlGy1iRZ3uEs4kG4HCvI7KRthUpM1vuHDPL98lidRIUaoRomkJtWtg==",
"engines": {
"node": ">0.6.x"
}
}
},
"dependencies": {
"@babel/runtime": {
"version": "7.17.8",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.8.tgz",
"integrity": "sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==",
"requires": {
"regenerator-runtime": "^0.13.4"
}
},
"automation-events": {
"version": "4.0.14",
"resolved": "https://registry.npmjs.org/automation-events/-/automation-events-4.0.14.tgz",
"integrity": "sha512-CB2Me0yW8sz7gSGwMiSfgfs1Oqlgs53k+eVESN6axvRyMAD3zlSp2nqndD2TQAtW3yOtSEJWNGsw0r48+f1wtw==",
"requires": {
"@babel/runtime": "^7.17.2",
"tslib": "^2.3.1"
}
},
"regenerator-runtime": {
"version": "0.13.9",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
},
"standardized-audio-context": {
"version": "25.3.21",
"resolved": "https://registry.npmjs.org/standardized-audio-context/-/standardized-audio-context-25.3.21.tgz",
"integrity": "sha512-CZEnayJJjNefeU+z1QGDhaid1LAAYxWYa2ipNk75ropwec9rq6fmclyhXb1wGtDsZ402irX3HLt1U/PwP9+1fA==",
"requires": {
"@babel/runtime": "^7.17.2",
"automation-events": "^4.0.14",
"tslib": "^2.3.1"
}
},
"tone": {
"version": "14.7.77",
"resolved": "https://registry.npmjs.org/tone/-/tone-14.7.77.tgz",
"integrity": "sha512-tCfK73IkLHyzoKUvGq47gyDyxiKLFvKiVCOobynGgBB9Dl0NkxTM2p+eRJXyCYrjJwy9Y0XCMqD3uOYsYt2Fdg==",
"requires": {
"standardized-audio-context": "^25.1.8",
"tslib": "^2.0.1"
}
},
"tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"webmidi": {
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/webmidi/-/webmidi-2.5.3.tgz",
"integrity": "sha512-PyMGvKcDGpvbQUfnmBORQJciyG3VAZ4aHlGy1iRZ3uEs4kG4HCvI7KRthUpM1vuHDPL98lidRIUaoRomkJtWtg=="
}
}
}
+14 -3
View File
@@ -1,8 +1,16 @@
{
"name": "@strudel.cycles/midi",
"version": "0.5.0",
"version": "0.6.0",
"description": "Midi API for strudel",
"main": "index.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": {
"build": "vite build",
"prepublishOnly": "npm run build"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tidalcycles/strudel.git"
@@ -21,8 +29,11 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/tone": "^0.5.0",
"tone": "^14.7.77",
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"webmidi": "^3.0.21"
},
"devDependencies": {
"vite": "^3.2.2"
}
}
+19
View File
@@ -0,0 +1,19 @@
import { defineConfig } from 'vite';
import { dependencies } from './package.json';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [],
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
},
rollupOptions: {
external: [...Object.keys(dependencies)],
},
target: 'esnext',
},
});
+1 -1
View File
@@ -40,5 +40,5 @@ The parser [krill-parser.js] is generated from [krill.pegjs](./krill.pegjs) usin
To generate the parser, run
```js
npm run build:parser
npm build:parser
```
-1031
View File
File diff suppressed because it is too large Load Diff
+12 -6
View File
@@ -1,12 +1,18 @@
{
"name": "@strudel.cycles/mini",
"version": "0.5.0",
"version": "0.6.0",
"description": "Mini notation for strudel",
"main": "index.mjs",
"type": "module",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": {
"test": "vitest run",
"build:parser": "peggy -o krill-parser.js --format es ./krill.pegjs"
"build:parser": "peggy -o krill-parser.js --format es ./krill.pegjs",
"build": "vite build",
"prepublishOnly": "npm run build"
},
"repository": {
"type": "git",
@@ -26,11 +32,11 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.5.0",
"@strudel.cycles/eval": "^0.5.0",
"@strudel.cycles/tone": "^0.5.0"
"@strudel.cycles/core": "workspace:*"
},
"devDependencies": {
"peggy": "^2.0.1"
"peggy": "^2.0.1",
"vite": "^3.2.2",
"vitest": "^0.25.7"
}
}
+19
View File
@@ -0,0 +1,19 @@
import { defineConfig } from 'vite';
import { dependencies } from './package.json';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [],
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
},
rollupOptions: {
external: [...Object.keys(dependencies)],
},
target: 'esnext',
},
});
+2 -2
View File
@@ -34,6 +34,6 @@ Now open the REPL and type:
s("<bd sd> hh").osc()
```
or just [click here](http://localhost:3000/#cygiPGJkIHNkPiBoaCIpLm9zYygp)...
or just [click here](https://strudel.tidalcycles.org/#cygiPGJkIHNkPiBoaCIpLm9zYygp)...
You can read more about [how to use Superdirt with Strudel the Tutorial](https://strudel.tidalcycles.org/learn/outputs#superdirt-api)
You can read more about [how to use Superdirt with Strudel the Tutorial](https://strudel.tidalcycles.org/learn/input-output/#superdirt-api)
-60
View File
@@ -1,60 +0,0 @@
{
"name": "@strudel.cycles/osc",
"version": "0.4.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@strudel.cycles/osc",
"version": "0.1.0",
"license": "AGPL-3.0-or-later",
"dependencies": {
"osc-js": "^2.3.2"
}
},
"node_modules/osc-js": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/osc-js/-/osc-js-2.3.2.tgz",
"integrity": "sha512-9i7J4u1hH+glooGMh+ki1ni0JGqKmylT8r0nXKugHbRK63rR+kl4O+5tGW6+/EszjbCju3KV+eXQQzFDdGrmhg==",
"dependencies": {
"ws": "^8.5.0"
}
},
"node_modules/ws": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
}
},
"dependencies": {
"osc-js": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/osc-js/-/osc-js-2.3.2.tgz",
"integrity": "sha512-9i7J4u1hH+glooGMh+ki1ni0JGqKmylT8r0nXKugHbRK63rR+kl4O+5tGW6+/EszjbCju3KV+eXQQzFDdGrmhg==",
"requires": {
"ws": "^8.5.0"
}
},
"ws": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
"requires": {}
}
}
}
+11 -4
View File
@@ -1,14 +1,19 @@
{
"name": "@strudel.cycles/osc",
"version": "0.4.0",
"version": "0.6.0",
"description": "OSC messaging for strudel",
"main": "osc.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": {
"test": "echo \"No tests present.\" && exit 0",
"server": "node server.js",
"tidal-sniffer": "node tidal-sniffer.js",
"client": "npx serve -p 4321",
"build": "npx pkg server.js --targets node16-macos-x64,node16-win-x64,node16-linux-x64 --out-path bin"
"build-bin": "npx pkg server.js --targets node16-macos-x64,node16-win-x64,node16-linux-x64 --out-path bin",
"build": "vite build",
"prepublishOnly": "npm run build"
},
"repository": {
"type": "git",
@@ -31,9 +36,11 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"osc-js": "^2.4.0"
},
"devDependencies": {
"pkg": "^5.7.0"
"pkg": "^5.7.0",
"vite": "^3.2.2"
}
}
+19
View File
@@ -0,0 +1,19 @@
import { defineConfig } from 'vite';
import { dependencies } from './package.json';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [],
build: {
lib: {
entry: resolve(__dirname, 'osc.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
},
rollupOptions: {
external: [...Object.keys(dependencies)],
},
target: 'esnext',
},
});
-2
View File
@@ -11,8 +11,6 @@ node_modules
dist-ssr
*.local
!dist
# Editor directories and files
.vscode/*
!.vscode/extensions.json
+1 -8
View File
@@ -33,11 +33,4 @@ export function Repl({ tune }) {
}
```
## Development
If you change something in here and want to see the changes in the repl, make sure to run `npm run build` inside this folder!
```js
npm run dev # dev server
npm run build # build package
```
For a more sophisticated example, check out the [nano-repl](./examples/nano-repl/)!
File diff suppressed because one or more lines are too long
-401
View File
@@ -1,401 +0,0 @@
import l, { useCallback as N, useRef as k, useEffect as _, useMemo as K, useState as w, useLayoutEffect as G } from "react";
import Z from "@uiw/react-codemirror";
import { Decoration as y, EditorView as J } from "@codemirror/view";
import { StateEffect as Q, StateField as X } from "@codemirror/state";
import { javascript as ee } from "@codemirror/lang-javascript";
import { tags as s } from "@lezer/highlight";
import { createTheme as te } from "@uiw/codemirror-themes";
import { repl as re, logger as ne, pianoroll as oe } from "@strudel.cycles/core";
import { webaudioOutput as ae, getAudioContext as ce } from "@strudel.cycles/webaudio";
import { useInView as se } from "react-hook-inview";
import { transpiler as ie } from "@strudel.cycles/transpiler";
const le = te({
theme: "dark",
settings: {
background: "#222",
foreground: "#75baff",
caret: "#ffcc00",
selection: "rgba(128, 203, 196, 0.5)",
selectionMatch: "#036dd626",
lineHighlight: "#00000050",
gutterBackground: "transparent",
gutterForeground: "#8a919966"
},
styles: [
{ tag: s.keyword, color: "#c792ea" },
{ tag: s.operator, color: "#89ddff" },
{ tag: s.special(s.variableName), color: "#eeffff" },
{ tag: s.typeName, color: "#c3e88d" },
{ tag: s.atom, color: "#f78c6c" },
{ tag: s.number, color: "#c3e88d" },
{ tag: s.definition(s.variableName), color: "#82aaff" },
{ tag: s.string, color: "#c3e88d" },
{ tag: s.special(s.string), color: "#c3e88d" },
{ tag: s.comment, color: "#7d8799" },
{ tag: s.variableName, color: "#c792ea" },
{ tag: s.tagName, color: "#c3e88d" },
{ tag: s.bracket, color: "#525154" },
{ tag: s.meta, color: "#ffcb6b" },
{ tag: s.attributeName, color: "#c792ea" },
{ tag: s.propertyName, color: "#c792ea" },
{ tag: s.className, color: "#decb6b" },
{ tag: s.invalid, color: "#ffffff" }
]
});
const j = Q.define(), ue = X.define({
create() {
return y.none;
},
update(e, r) {
try {
for (let t of r.effects)
if (t.is(j))
if (t.value) {
const a = y.mark({ attributes: { style: "background-color: #FFCA2880" } });
e = y.set([a.range(0, r.newDoc.length)]);
} else
e = y.set([]);
return e;
} catch (t) {
return console.warn("flash error", t), e;
}
},
provide: (e) => J.decorations.from(e)
}), de = (e) => {
e.dispatch({ effects: j.of(!0) }), setTimeout(() => {
e.dispatch({ effects: j.of(!1) });
}, 200);
}, z = Q.define(), fe = X.define({
create() {
return y.none;
},
update(e, r) {
try {
for (let t of r.effects)
if (t.is(z)) {
const a = t.value.map(
(o) => (o.context.locations || []).map(({ start: i, end: u }) => {
const m = o.context.color || "#FFCA28";
let n = r.newDoc.line(i.line).from + i.column, d = r.newDoc.line(u.line).from + u.column;
const v = r.newDoc.length;
return n > v || d > v ? void 0 : y.mark({ attributes: { style: `outline: 1.5px solid ${m};` } }).range(n, d);
})
).flat().filter(Boolean) || [];
e = y.set(a, !0);
}
return e;
} catch {
return y.set([]);
}
},
provide: (e) => J.decorations.from(e)
}), me = [ee(), le, fe, ue];
function ge({ value: e, onChange: r, onViewChanged: t, onSelectionChange: a, options: o, editorDidMount: i }) {
const u = N(
(d) => {
r?.(d);
},
[r]
), m = N(
(d) => {
t?.(d);
},
[t]
), n = N(
(d) => {
d.selectionSet && a && a?.(d.state.selection);
},
[a]
);
return /* @__PURE__ */ l.createElement(l.Fragment, null, /* @__PURE__ */ l.createElement(Z, {
value: e,
onChange: u,
onCreateEditor: m,
onUpdate: n,
extensions: me
}));
}
function W(...e) {
return e.filter(Boolean).join(" ");
}
function pe({ view: e, pattern: r, active: t, getTime: a }) {
const o = k([]), i = k();
_(() => {
if (e)
if (r && t) {
let u = requestAnimationFrame(function m() {
try {
const n = a(), v = [Math.max(i.current || n, n - 1 / 10, 0), n + 1 / 60];
i.current = v[1], o.current = o.current.filter((p) => p.whole.end > n);
const h = r.queryArc(...v).filter((p) => p.hasOnset());
o.current = o.current.concat(h), e.dispatch({ effects: z.of(o.current) });
} catch {
e.dispatch({ effects: z.of([]) });
}
u = requestAnimationFrame(m);
});
return () => {
cancelAnimationFrame(u);
};
} else
o.current = [], e.dispatch({ effects: z.of([]) });
}, [r, t, e]);
}
function he(e, r = !1) {
const t = k(), a = k(), o = (m) => {
if (a.current !== void 0) {
const n = m - a.current;
e(m, n);
}
a.current = m, t.current = requestAnimationFrame(o);
}, i = () => {
t.current = requestAnimationFrame(o);
}, u = () => {
t.current && cancelAnimationFrame(t.current), delete t.current;
};
return _(() => {
t.current && (u(), i());
}, [e]), _(() => (r && i(), u), []), {
start: i,
stop: u
};
}
function ve({ pattern: e, started: r, getTime: t, onDraw: a }) {
let o = k([]), i = k(null);
const { start: u, stop: m } = he(
N(() => {
const n = t();
if (i.current === null) {
i.current = n;
return;
}
const d = e.queryArc(Math.max(i.current, n - 1 / 10), n), v = 4;
i.current = n, o.current = (o.current || []).filter((h) => h.whole.end > n - v).concat(d.filter((h) => h.hasOnset())), a(n, o.current);
}, [e])
);
_(() => {
r ? u() : (o.current = [], m());
}, [r]);
}
function be(e) {
return _(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), N((r) => window.postMessage(r, "*"), []);
}
function Ee({
defaultOutput: e,
interval: r,
getTime: t,
evalOnMount: a = !1,
initialCode: o = "",
autolink: i = !1,
beforeEval: u,
afterEval: m,
editPattern: n,
onEvalError: d,
onToggle: v,
canvasId: h
}) {
const p = K(() => we(), []);
h = h || `canvas-${p}`;
const [F, A] = w(), [P, D] = w(), [b, q] = w(o), [x, V] = w(), [I, R] = w(), [L, B] = w(!1), H = b !== x, { scheduler: M, evaluate: c, start: f, stop: C, pause: O } = K(
() => re({
interval: r,
defaultOutput: e,
onSchedulerError: A,
onEvalError: (g) => {
D(g), d?.(g);
},
getTime: t,
transpiler: ie,
beforeEval: ({ code: g }) => {
q(g), u?.();
},
editPattern: n ? (g) => n(g, p) : void 0,
afterEval: ({ pattern: g, code: T }) => {
V(T), R(g), D(), A(), i && (window.location.hash = "#" + encodeURIComponent(btoa(T))), m?.();
},
onToggle: (g) => {
B(g), v?.(g);
}
}),
[e, r, t]
), Y = be(({ data: { from: g, type: T } }) => {
T === "start" && g !== p && C();
}), S = N(
async (g = !0) => {
await c(b, g), Y({ type: "start", from: p });
},
[c, b]
), U = k();
return _(() => {
!U.current && a && b && (U.current = !0, S());
}, [S, a, b]), _(() => () => {
M.stop();
}, [M]), {
id: p,
canvasId: h,
code: b,
setCode: q,
error: F || P,
schedulerError: F,
scheduler: M,
evalError: P,
evaluate: c,
activateCode: S,
activeCode: x,
isDirty: H,
pattern: I,
started: L,
start: f,
stop: C,
pause: O,
togglePlay: async () => {
L ? M.pause() : await S();
}
};
}
function we() {
return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
}
function $({ type: e }) {
return /* @__PURE__ */ l.createElement("svg", {
xmlns: "http://www.w3.org/2000/svg",
className: "sc-h-5 sc-w-5",
viewBox: "0 0 20 20",
fill: "currentColor"
}, {
refresh: /* @__PURE__ */ l.createElement("path", {
fillRule: "evenodd",
d: "M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z",
clipRule: "evenodd"
}),
play: /* @__PURE__ */ l.createElement("path", {
fillRule: "evenodd",
d: "M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z",
clipRule: "evenodd"
}),
pause: /* @__PURE__ */ l.createElement("path", {
fillRule: "evenodd",
d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 012 0v4a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v4a1 1 0 102 0V8a1 1 0 00-1-1z",
clipRule: "evenodd"
})
}[e]);
}
const ye = "_container_3i85k_1", ke = "_header_3i85k_5", _e = "_buttons_3i85k_9", Fe = "_button_3i85k_9", Ce = "_buttonDisabled_3i85k_17", Ne = "_error_3i85k_21", xe = "_body_3i85k_25", E = {
container: ye,
header: ke,
buttons: _e,
button: Fe,
buttonDisabled: Ce,
error: Ne,
body: xe
}, Me = () => ce().currentTime;
function je({ tune: e, hideOutsideView: r = !1, enableKeyboard: t, withCanvas: a = !1, canvasHeight: o = 200 }) {
const {
code: i,
setCode: u,
evaluate: m,
activateCode: n,
error: d,
isDirty: v,
activeCode: h,
pattern: p,
started: F,
scheduler: A,
togglePlay: P,
stop: D,
canvasId: b,
id: q
} = Ee({
initialCode: e,
defaultOutput: ae,
getTime: Me,
editPattern: (c, f) => c.withContext((C) => ({ ...C, id: f }))
});
ve({
pattern: p,
started: a && F,
getTime: () => A.now(),
onDraw: (c, f) => {
const C = document.querySelector("#" + b).getContext("2d");
oe({ ctx: C, time: c, haps: f, autorange: 1, fold: 1, playhead: 1 });
}
});
const [x, V] = w(), [I, R] = se({
threshold: 0.01
}), L = k(), B = K(() => ((R || !r) && (L.current = !0), R || L.current), [R, r]);
pe({
view: x,
pattern: p,
active: F && !h?.includes("strudel disable-highlighting"),
getTime: () => A.getPhase()
}), G(() => {
if (t) {
const c = async (f) => {
(f.ctrlKey || f.altKey) && (f.code === "Enter" ? (f.preventDefault(), de(x), await n()) : f.code === "Period" && (D(), f.preventDefault()));
};
return window.addEventListener("keydown", c, !0), () => window.removeEventListener("keydown", c, !0);
}
}, [t, p, i, m, D, x]);
const [H, M] = w([]);
return Ae(
N((c) => {
const { data: f } = c.detail;
f?.hap?.context?.id === q && M((O) => O.concat([c.detail]).slice(-10));
}, [])
), /* @__PURE__ */ l.createElement("div", {
className: E.container,
ref: I
}, /* @__PURE__ */ l.createElement("div", {
className: E.header
}, /* @__PURE__ */ l.createElement("div", {
className: E.buttons
}, /* @__PURE__ */ l.createElement("button", {
className: W(E.button, F ? "sc-animate-pulse" : ""),
onClick: () => P()
}, /* @__PURE__ */ l.createElement($, {
type: F ? "pause" : "play"
})), /* @__PURE__ */ l.createElement("button", {
className: W(v ? E.button : E.buttonDisabled),
onClick: () => n()
}, /* @__PURE__ */ l.createElement($, {
type: "refresh"
}))), d && /* @__PURE__ */ l.createElement("div", {
className: E.error
}, d.message)), /* @__PURE__ */ l.createElement("div", {
className: E.body
}, B && /* @__PURE__ */ l.createElement(ge, {
value: i,
onChange: u,
onViewChanged: V
})), a && /* @__PURE__ */ l.createElement("canvas", {
id: b,
className: "w-full pointer-events-none",
height: o,
ref: (c) => {
c && c.width !== c.clientWidth && (c.width = c.clientWidth);
}
}), !!H.length && /* @__PURE__ */ l.createElement("div", {
className: "sc-bg-gray-800 sc-rounded-md sc-p-2"
}, H.map(({ message: c }, f) => /* @__PURE__ */ l.createElement("div", {
key: f
}, c))));
}
function Ae(e) {
De(ne.key, e);
}
function De(e, r, t = !1) {
_(() => (document.addEventListener(e, r, t), () => {
document.removeEventListener(e, r, t);
}), [r]);
}
const Ue = (e) => G(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]);
export {
ge as CodeMirror,
je as MiniRepl,
W as cx,
de as flash,
pe as useHighlighting,
Ue as useKeydown,
be as usePostMessage,
Ee as useStrudel
};
-1
View File
@@ -1 +0,0 @@
.cm-editor{background-color:transparent!important;height:100%;z-index:11;font-size:18px}.cm-theme-light{width:100%}.cm-line>*{background:#00000095}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.sc-h-5{height:1.25rem}.sc-w-5{width:1.25rem}@keyframes sc-pulse{50%{opacity:.5}}.sc-animate-pulse{animation:sc-pulse 2s cubic-bezier(.4,0,.6,1) infinite}.sc-rounded-md{border-radius:.375rem}.sc-bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity))}.sc-p-2{padding:.5rem}._container_3i85k_1{overflow:hidden;border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(34 34 34 / var(--tw-bg-opacity))}._header_3i85k_5{display:flex;justify-content:space-between;border-top-width:1px;--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}._buttons_3i85k_9{display:flex}._button_3i85k_9{display:flex;width:4rem;cursor:pointer;align-items:center;justify-content:center;border-right-width:1px;--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity));padding:.25rem;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}._button_3i85k_9:hover{--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity))}._buttonDisabled_3i85k_17{display:flex;width:4rem;cursor:pointer;cursor:not-allowed;align-items:center;justify-content:center;--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity));padding:.25rem;--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}._error_3i85k_21{padding:.25rem;text-align:right;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity))}._body_3i85k_25{position:relative;overflow:auto}
+1 -1
View File
@@ -8,7 +8,7 @@ pnpm-debug.log*
lerna-debug.log*
node_modules
!dist
dist
dist-ssr
*.local
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
.cm-editor{background-color:transparent!important;height:100%;z-index:11;font-size:16px}.cm-theme-light{width:100%}.cm-line>*{background:#00000095}*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input:-ms-input-placeholder,textarea:-ms-input-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.fixed{position:fixed}.absolute{position:absolute}.bottom-0{bottom:0px}.z-\[12\]{z-index:12}.flex{display:flex}.w-full{width:100%}.justify-center{justify-content:center}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.rounded-t-md{border-top-left-radius:.375rem;border-top-right-radius:.375rem}.bg-slate-500{--tw-bg-opacity: 1;background-color:rgb(100 116 139 / var(--tw-bg-opacity))}.px-2{padding-left:.5rem;padding-right:.5rem}body{background:#123}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
import{f as s,i as t,h as o,j as d,q as f,l as p,k as u,p as i,o as l,n as r,w as g}from"./index.ec9f9930.js";export{s as getAudioContext,t as getCachedBuffer,o as getDestination,d as getLoadedBuffer,f as getLoadedSamples,p as loadBuffer,u as loadGithubSamples,i as panic,l as resetLoadedSamples,r as samples,g as webaudioOutput};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
import{P as w}from"./index.ec9f9930.js";var i,f=!1;async function b(a=38400){if(!f){if(f=!0,i)return i;if("serial"in navigator){const r=await navigator.serial.requestPort();await r.open({baudRate:a});const o=new TextEncoderStream;o.readable.pipeTo(r.writable);const s=o.writable.getWriter();i=function(e){s.write(e)}}else throw"Webserial is not available in this browser."}}const g=.1;w.prototype.serial=function(...a){return this._withHap(r=>{i||b(...a);const o=(s,e,u)=>{var t="";if(typeof e.value=="object")if("action"in e.value){t+=e.value.action+"(";var c=!0;for(const[n,l]of Object.entries(e.value))n!=="action"&&(c?c=!1:t+=",",t+=`${n}:${l}`);t+=")"}else for(const[n,l]of Object.entries(e.value))t+=`${n}:${l};`;else t=e.value;const v=(s-u+g)*1e3;window.setTimeout(i,v,t)};return r.setContext({...r.context,onTrigger:o})})};export{b as getWriter};
-14
View File
@@ -1,14 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Strudel Nano REPL</title>
<script type="module" crossorigin src="./assets/index.ec9f9930.js"></script>
<link rel="stylesheet" href="./assets/index.75f8960b.css">
</head>
<body>
<div id="root"></div>
</body>
</html>
File diff suppressed because it is too large Load Diff
+10 -3
View File
@@ -1,7 +1,7 @@
{
"name": "nano-repl",
"name": "@strudel.cycles/nano-repl",
"private": true,
"version": "0.0.0",
"version": "0.6.0",
"type": "module",
"scripts": {
"dev": "vite",
@@ -10,7 +10,14 @@
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/osc": "workspace:*",
"@strudel.cycles/mini": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"@strudel.cycles/tonal": "workspace:*",
"@strudel.cycles/react": "workspace:*"
},
"devDependencies": {
"@types/react": "^18.0.17",
+9 -17
View File
@@ -1,27 +1,21 @@
import { evalScope, controls } from '@strudel.cycles/core';
import { getAudioContext, panic, webaudioOutput } from '@strudel.cycles/webaudio';
import { controls, evalScope } from '@strudel.cycles/core';
import { CodeMirror, useHighlighting, useKeydown, useStrudel, flash } from '@strudel.cycles/react';
import { getAudioContext, initAudioOnFirstClick, panic, webaudioOutput } from '@strudel.cycles/webaudio';
import { useCallback, useState } from 'react';
import CodeMirror, { flash } from '../../../src/components/CodeMirror6';
import useKeydown from '../../../src/hooks/useKeydown.mjs';
import useStrudel from '../../../src/hooks/useStrudel';
import useHighlighting from '../../../src/hooks/useHighlighting';
import './style.css';
// import { prebake } from '../../../../../repl/src/prebake.mjs';
initAudioOnFirstClick();
// TODO: only import stuff when play is pressed?
evalScope(
controls,
import('@strudel.cycles/core'),
// import('@strudel.cycles/tone'),
// import('@strudel.cycles/midi'), // TODO: find out why midi loads tone.js
import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'),
import('@strudel.cycles/xen'),
import('@strudel.cycles/webaudio'),
import('@strudel.cycles/osc'),
import('@strudel.cycles/webdirt'),
import('@strudel.cycles/serial'),
import('@strudel.cycles/soundfonts'),
);
const defaultTune = `samples({
@@ -30,7 +24,7 @@ const defaultTune = `samples({
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
}, 'github:tidalcycles/Dirt-Samples/master/');
stack(
s("bd,[~ <sd!3 sd(3,4,2)>],hh(3,4)") // drums
s("bd,[~ <sd!3 sd(3,4,2)>],hh*8") // drums
.speed(perlin.range(.7,.9)) // random sample speed variation
//.hush()
,"<a1 b1*2 a1(3,8) e2>" // bassline
@@ -43,7 +37,7 @@ stack(
.gain(.4) // turn down
.cutoff(sine.slow(7).range(300,5000)) // automate cutoff
//.hush()
,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings() // chords
,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings('lefthand') // chords
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice
.add(perlin.range(0,.5)) // random pitch variation
.n() // wrap in "n"
@@ -103,7 +97,7 @@ function App() {
scheduler.start();
}
} else if (e.code === 'Period') {
scheduler.pause();
scheduler.stop();
panic();
e.preventDefault();
}
@@ -114,13 +108,11 @@ function App() {
);
return (
<div>
{/* <textarea value={code} onChange={(e) => setCode(e.target.value)} cols="64" rows="30" /> */}
<nav className="z-[12] w-full flex justify-center absolute bottom-0">
<nav className="z-[12] w-full flex justify-center fixed bottom-0">
<div className="bg-slate-500 space-x-2 px-2 rounded-t-md">
<button
onClick={async () => {
await evaluate(code);
await getAudioContext().resume();
scheduler.start();
}}
>
+13 -17
View File
@@ -1,24 +1,18 @@
{
"name": "@strudel.cycles/react",
"version": "0.5.0",
"version": "0.6.0",
"description": "React components for strudel",
"main": "dist/index.cjs.js",
"module": "dist/index.es.js",
"exports": {
".": {
"require": "./dist/index.cjs.js",
"import": "./dist/index.es.js"
},
"./dist/style.css": "./dist/style.css"
"main": "src/index.js",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"files": [
"dist"
],
"scripts": {
"dev": "vite",
"build": "vite build",
"watch": "vite build --watch",
"preview": "vite preview"
"preview": "vite preview",
"prepublishOnly": "npm run build"
},
"repository": {
"type": "git",
@@ -39,10 +33,12 @@
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@codemirror/lang-javascript": "^6.1.1",
"@strudel.cycles/core": "^0.5.0",
"@strudel.cycles/tone": "^0.5.0",
"@strudel.cycles/transpiler": "^0.5.0",
"@strudel.cycles/webaudio": "^0.5.0",
"@codemirror/state": "^6.2.0",
"@codemirror/view": "^6.7.3",
"@lezer/highlight": "^1.1.3",
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"@uiw/codemirror-themes": "^4.12.4",
"@uiw/react-codemirror": "^4.12.4",
"react-hook-inview": "^4.5.0"
-1
View File
@@ -6,7 +6,6 @@ import { controls, evalScope } from '@strudel.cycles/core';
evalScope(
controls,
import('@strudel.cycles/core'),
// import('@strudel.cycles/tone'),
import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'),
import('@strudel.cycles/midi'),
+8 -1
View File
@@ -2,7 +2,7 @@ import React from 'react';
export function Icon({ type }) {
return (
<svg xmlns="http://www.w3.org/2000/svg" className="sc-h-5 sc-w-5" viewBox="0 0 20 20" fill="currentColor">
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
{
{
refresh: (
@@ -26,6 +26,13 @@ export function Icon({ type }) {
clipRule="evenodd"
/>
),
stop: (
<path
fillRule="evenodd"
d="M2 10a8 8 0 1116 0 8 8 0 01-16 0zm5-2.25A.75.75 0 017.75 7h4.5a.75.75 0 01.75.75v4.5a.75.75 0 01-.75.75h-4.5a.75.75 0 01-.75-.75v-4.5z"
clipRule="evenodd"
/>
),
}[type]
}
</svg>
+16 -24
View File
@@ -1,11 +1,9 @@
import { pianoroll } from '@strudel.cycles/core';
import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio';
import React, { useLayoutEffect, useMemo, useRef, useState, useCallback, useEffect } from 'react';
import { useInView } from 'react-hook-inview';
import 'tailwindcss/tailwind.css';
import cx from '../cx';
import useHighlighting from '../hooks/useHighlighting.mjs';
import usePatternFrame from '../hooks/usePatternFrame.mjs';
import useStrudel from '../hooks/useStrudel.mjs';
import CodeMirror6, { flash } from './CodeMirror6';
import { Icon } from './Icon';
@@ -15,7 +13,13 @@ import { logger } from '@strudel.cycles/core';
const getTime = () => getAudioContext().currentTime;
export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, withCanvas = false, canvasHeight = 200 }) {
export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, drawTime, punchcard, canvasHeight = 200 }) {
drawTime = drawTime || (punchcard ? [0, 4] : undefined);
const evalOnMount = !!drawTime;
const drawContext = useCallback(
!!drawTime ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null,
[drawTime],
);
const {
code,
setCode,
@@ -34,25 +38,13 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, withCa
} = useStrudel({
initialCode: tune,
defaultOutput: webaudioOutput,
editPattern: (pat) => (punchcard ? pat.punchcard() : pat),
getTime,
editPattern: (pat, id) => {
return pat.withContext((ctx) => ({ ...ctx, id }));
},
evalOnMount,
drawContext,
drawTime,
});
usePatternFrame({
pattern,
started: withCanvas && started,
getTime: () => scheduler.now(),
onDraw: (time, haps) => {
const ctx = document.querySelector('#' + canvasId).getContext('2d');
pianoroll({ ctx, time, haps, autorange: 1, fold: 1, playhead: 1 });
},
});
/* useEffect(() => {
init && activateCode();
}, [init, activateCode]); */
const [view, setView] = useState();
const [ref, isVisible] = useInView({
threshold: 0.01,
@@ -68,7 +60,7 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, withCa
view,
pattern,
active: started && !activeCode?.includes('strudel disable-highlighting'),
getTime: () => scheduler.getPhase(),
getTime: () => scheduler.now(),
});
// set active pattern on ctrl+enter
@@ -109,8 +101,8 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, withCa
<div className={styles.container} ref={ref}>
<div className={styles.header}>
<div className={styles.buttons}>
<button className={cx(styles.button, started ? 'sc-animate-pulse' : '')} onClick={() => togglePlay()}>
<Icon type={started ? 'pause' : 'play'} />
<button className={cx(styles.button, started ? 'animate-pulse' : '')} onClick={() => togglePlay()}>
<Icon type={started ? 'stop' : 'play'} />
</button>
<button className={cx(isDirty ? styles.button : styles.buttonDisabled)} onClick={() => activateCode()}>
<Icon type="refresh" />
@@ -121,7 +113,7 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, withCa
<div className={styles.body}>
{show && <CodeMirror6 value={code} onChange={setCode} onViewChanged={setView} />}
</div>
{withCanvas && (
{drawTime && (
<canvas
id={canvasId}
className="w-full pointer-events-none"
@@ -134,7 +126,7 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, withCa
></canvas>
)}
{!!log.length && (
<div className="sc-bg-gray-800 sc-rounded-md sc-p-2">
<div className="bg-gray-800 rounded-md p-2">
{log.map(({ message }, i) => (
<div key={i}>{message}</div>
))}
@@ -1,27 +1,27 @@
.container {
@apply sc-rounded-md sc-overflow-hidden sc-bg-[#222222];
@apply rounded-md overflow-hidden bg-[#222222];
}
.header {
@apply sc-flex sc-justify-between sc-bg-slate-700 sc-border-t sc-border-slate-500;
@apply flex justify-between bg-slate-700 border-t border-slate-500;
}
.buttons {
@apply sc-flex;
@apply flex;
}
.button {
@apply sc-cursor-pointer sc-w-16 sc-flex sc-items-center sc-justify-center sc-p-1 sc-bg-slate-700 sc-border-r sc-border-slate-500 sc-text-white hover:sc-bg-slate-600;
@apply cursor-pointer w-16 flex items-center justify-center p-1 bg-slate-700 border-r border-slate-500 text-white hover:bg-slate-600;
}
.buttonDisabled {
@apply sc-cursor-pointer sc-w-16 sc-flex sc-items-center sc-justify-center sc-p-1 sc-bg-slate-600 sc-text-slate-400 sc-cursor-not-allowed;
@apply w-16 flex items-center justify-center p-1 bg-slate-600 text-slate-400 cursor-not-allowed;
}
.error {
@apply sc-text-right sc-p-1 sc-text-sm sc-text-red-200;
@apply text-right p-1 text-sm text-red-200;
}
.body {
@apply sc-overflow-auto sc-relative;
@apply overflow-auto relative;
}
+3 -2
View File
@@ -3,16 +3,17 @@ import { setHighlights } from '../components/CodeMirror6';
function useHighlighting({ view, pattern, active, getTime }) {
const highlights = useRef([]);
const lastEnd = useRef();
const lastEnd = useRef(0);
useEffect(() => {
if (view) {
if (pattern && active) {
lastEnd.current = 0;
let frame = requestAnimationFrame(function updateHighlights() {
try {
const audioTime = getTime();
// force min framerate of 10 fps => fixes crash on tab refocus, where lastEnd could be far away
// see https://github.com/tidalcycles/strudel/issues/108
const begin = Math.max(lastEnd.current || audioTime, audioTime - 1 / 10, 0); // negative time seems buggy
const begin = Math.max(lastEnd.current ?? audioTime, audioTime - 1 / 10, -0.01); // negative time seems buggy
const span = [begin, audioTime + 1 / 60];
lastEnd.current = span[1];
highlights.current = highlights.current.filter((hap) => hap.whole.end > audioTime); // keep only highlights that are still active
+19 -5
View File
@@ -2,23 +2,32 @@ import { useCallback, useEffect, useRef } from 'react';
import 'tailwindcss/tailwind.css';
import useFrame from '../hooks/useFrame.mjs';
function usePatternFrame({ pattern, started, getTime, onDraw }) {
function usePatternFrame({ pattern, started, getTime, onDraw, drawTime = [-2, 2] }) {
let [lookbehind, lookahead] = drawTime;
lookbehind = Math.abs(lookbehind);
let visibleHaps = useRef([]);
let lastFrame = useRef(null);
useEffect(() => {
if (pattern && started) {
const t = getTime();
const futureHaps = pattern.queryArc(Math.max(t, 0), t + lookahead + 0.1); // +0.1 = workaround for weird holes in query..
visibleHaps.current = visibleHaps.current.filter((h) => h.whole.begin < t);
visibleHaps.current = visibleHaps.current.concat(futureHaps);
}
}, [pattern, started]);
const { start: startFrame, stop: stopFrame } = useFrame(
useCallback(() => {
const phase = getTime();
const phase = getTime() + lookahead;
if (lastFrame.current === null) {
lastFrame.current = phase;
return;
}
const haps = pattern.queryArc(Math.max(lastFrame.current, phase - 1 / 10), phase);
const cycles = 4;
lastFrame.current = phase;
visibleHaps.current = (visibleHaps.current || [])
.filter((h) => h.whole.end > phase - cycles) // in frame
.filter((h) => h.whole.end >= phase - lookbehind - lookahead) // in frame
.concat(haps.filter((h) => h.hasOnset()));
onDraw(phase, visibleHaps.current);
onDraw(pattern, phase - lookahead, visibleHaps.current, drawTime);
}, [pattern]),
);
useEffect(() => {
@@ -29,6 +38,11 @@ function usePatternFrame({ pattern, started, getTime, onDraw }) {
stopFrame();
}
}, [started]);
return {
clear: () => {
visibleHaps.current = [];
},
};
}
export default usePatternFrame;
+42 -5
View File
@@ -1,6 +1,7 @@
import { useRef, useCallback, useEffect, useMemo, useState } from 'react';
import { repl } from '@strudel.cycles/core';
import { transpiler } from '@strudel.cycles/transpiler';
import usePatternFrame from './usePatternFrame';
import usePostMessage from './usePostMessage.mjs';
function useStrudel({
@@ -16,6 +17,8 @@ function useStrudel({
onEvalError,
onToggle,
canvasId,
drawContext,
drawTime = [-2, 2],
}) {
const id = useMemo(() => s4(), []);
canvasId = canvasId || `canvas-${id}`;
@@ -27,6 +30,7 @@ function useStrudel({
const [pattern, setPattern] = useState();
const [started, setStarted] = useState(false);
const isDirty = code !== activeCode;
const shouldPaint = useCallback((pat) => !!(pat?.context?.onPaint && drawContext), [drawContext]);
// TODO: make sure this hook reruns when scheduler.started changes
const { scheduler, evaluate, start, stop, pause } = useMemo(
@@ -40,12 +44,13 @@ function useStrudel({
onEvalError?.(err);
},
getTime,
drawContext,
transpiler,
editPattern,
beforeEval: ({ code }) => {
setCode(code);
beforeEval?.();
},
editPattern: editPattern ? (pat) => editPattern(pat, id) : undefined,
afterEval: ({ pattern: _pattern, code }) => {
setActiveCode(code);
setPattern(_pattern);
@@ -71,19 +76,41 @@ function useStrudel({
});
const activateCode = useCallback(
async (autostart = true) => {
await evaluate(code, autostart);
const res = await evaluate(code, autostart);
broadcast({ type: 'start', from: id });
return res;
},
[evaluate, code],
);
const onDraw = useCallback(
(pattern, time, haps, drawTime) => {
const { onPaint } = pattern.context || {};
const ctx = typeof drawContext === 'function' ? drawContext(canvasId) : drawContext;
onPaint?.(ctx, time, haps, drawTime);
},
[drawContext, canvasId],
);
const drawFirstFrame = useCallback(
(pat) => {
if (shouldPaint(pat)) {
const [_, lookahead] = drawTime;
const haps = pat.queryArc(0, lookahead);
// draw at -0.001 to avoid activating haps at 0
onDraw(pat, -0.001, haps, drawTime);
}
},
[drawTime, onDraw, shouldPaint],
);
const inited = useRef();
useEffect(() => {
if (!inited.current && evalOnMount && code) {
inited.current = true;
activateCode();
evaluate(code, false).then((pat) => drawFirstFrame(pat));
}
}, [activateCode, evalOnMount, code]);
}, [evalOnMount, code, evaluate, drawFirstFrame]);
// this will stop the scheduler when hot reloading in development
useEffect(() => {
@@ -94,12 +121,22 @@ function useStrudel({
const togglePlay = async () => {
if (started) {
scheduler.pause();
scheduler.stop();
drawFirstFrame(pattern);
} else {
await activateCode();
}
};
const error = schedulerError || evalError;
usePatternFrame({
pattern,
started: shouldPaint(pattern) && started,
getTime: () => scheduler.now(),
drawTime,
onDraw,
});
return {
id,
canvasId,
-1
View File
@@ -7,5 +7,4 @@ module.exports = {
corePlugins: {
preflight: false,
},
prefix: 'sc-',
};
+1 -1
View File
@@ -14,7 +14,7 @@ export default defineConfig({
lib: {
entry: resolve(__dirname, 'src', 'index.js'),
formats: ['es', 'cjs'],
fileName: (ext) => `index.${ext}.js`,
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
// for UMD name: 'GlobalName'
},
rollupOptions: {
-1
View File
@@ -1,4 +1,3 @@
# @strudel.cycles/serial
This package adds webserial functionality to strudel Patterns, for e.g. sending messages to arduino microcontrollers.
+16 -2
View File
@@ -1,8 +1,16 @@
{
"name": "@strudel.cycles/serial",
"version": "0.3.0",
"version": "0.6.0",
"description": "Webserial API for strudel",
"main": "serial.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": {
"build": "vite build",
"prepublishOnly": "npm run build"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tidalcycles/strudel.git"
@@ -19,5 +27,11 @@
"bugs": {
"url": "https://github.com/tidalcycles/strudel/issues"
},
"homepage": "https://github.com/tidalcycles/strudel#readme"
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "workspace:*"
},
"devDependencies": {
"vite": "^3.2.2"
}
}
+19
View File
@@ -0,0 +1,19 @@
import { defineConfig } from 'vite';
import { dependencies } from './package.json';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [],
build: {
lib: {
entry: resolve(__dirname, 'serial.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
},
rollupOptions: {
external: [...Object.keys(dependencies)],
},
target: 'esnext',
},
});
-301
View File
@@ -1,301 +0,0 @@
{
"name": "@strudel.cycles/soundfonts",
"version": "0.5.0",
"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=="
}
}
}
+13 -4
View File
@@ -1,8 +1,16 @@
{
"name": "@strudel.cycles/soundfonts",
"version": "0.5.0",
"version": "0.6.0",
"description": "Soundsfont support for strudel",
"main": "index.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": {
"build": "vite build",
"prepublishOnly": "npm run build"
},
"type": "module",
"repository": {
"type": "git",
@@ -22,12 +30,13 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.5.0",
"@strudel.cycles/webaudio": "^0.5.0",
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"sfumato": "^0.1.2",
"soundfont2": "^0.4.0"
},
"devDependencies": {
"node-fetch": "^3.2.6"
"node-fetch": "^3.2.6",
"vite": "^3.2.2"
}
}
+19
View File
@@ -0,0 +1,19 @@
import { defineConfig } from 'vite';
import { dependencies } from './package.json';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [],
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
},
rollupOptions: {
external: [...Object.keys(dependencies)],
},
target: 'esnext',
},
});
-2694
View File
File diff suppressed because it is too large Load Diff
+15 -5
View File
@@ -1,12 +1,18 @@
{
"name": "@strudel.cycles/tonal",
"version": "0.5.0",
"version": "0.6.0",
"description": "Tonal functions for strudel",
"main": "index.mjs",
"type": "module",
"scripts": {
"test": "vitest run"
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": {
"build": "vite build",
"test": "vitest run",
"prepublishOnly": "npm run build"
},
"type": "module",
"repository": {
"type": "git",
"url": "git+https://github.com/tidalcycles/strudel.git"
@@ -25,9 +31,13 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.5.0",
"@strudel.cycles/core": "workspace:*",
"@tonaljs/tonal": "^4.7.2",
"chord-voicings": "^0.0.1",
"webmidi": "^3.0.21"
},
"devDependencies": {
"vite": "^3.2.2",
"vitest": "^0.25.7"
}
}
+11
View File
@@ -0,0 +1,11 @@
import { describe, it, expect } from 'vitest';
import { Note } from '../tonleiter.mjs';
describe('tonleiter', () => {
it('Should tokenize notes', () => {
expect(Note.tokenize('c')).toEqual(['c', '']);
expect(Note.tokenize('C')).toEqual(['C', '']);
expect(Note.tokenize('c#')).toEqual(['c', '#']);
expect(Note.tokenize('Bb')).toEqual(['B', 'b']);
});
});
+3
View File
@@ -148,6 +148,9 @@ export const scale = register('scale', function (scale /* : string */, pat) {
let [tonic, scaleName] = Scale.tokenize(scale);
const { pc, oct = 3 } = Note.get(tonic);
note = scaleOffset(pc + ' ' + scaleName, asNumber, pc + oct);
/* const scaleWIthOctave = `${pc + oct} ${scaleName}`;
const degree = Scale.degrees(scaleWIthOctave);
note = degree(asNumber + (asNumber >= 0 ? 1 : 0)); */
}
return hap.withValue(() => (isObject ? { ...hap.value, note } : note)).setContext({ ...hap.context, scale });
});
+71
View File
@@ -0,0 +1,71 @@
const steps = [1, 0, 2, 0, 3, 4, 0, 5, 0, 6, 0, 7];
const notes = ['C', '', 'D', '', 'E', 'F', '', 'G', '', 'A', '', 'B'];
const noteLetters = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];
const accidentalOffset = (accidentals) => {
return accidentals.split('#').length - accidentals.split('b').length;
};
const accidentalString = (offset) => {
if (offset < 0) {
return 'b'.repeat(-offset);
}
if (offset > 0) {
return '#'.repeat(offset);
}
return '';
};
export const Step = {
tokenize(step) {
const [accidentals, stepNumber] = step.match(/^([#b]*)([1-9]+)$/).slice(1);
return [accidentals, parseInt(stepNumber)];
},
accidentals(step) {
return accidentalOffset(Step.tokenize(step)[0]);
},
};
Step.tokenize('#11');
Step.tokenize('b13');
Step.tokenize('bb6');
Step.accidentals('3');
Step.accidentals('b3');
export const Note = {
// TODO: support octave numbers
tokenize(note) {
return [note[0], note.slice(1)];
},
accidentals(note) {
return accidentalOffset(this.tokenize(note)[1]);
},
};
Note.tokenize('C##');
Note.accidentals('C#');
Note.accidentals('C##');
Note.accidentals('Eb');
Note.accidentals('Bbb');
// TODO: support octave numbers
function transpose(note, step) {
// example: E, 3
const stepNumber = Step.tokenize(step)[1]; // 3
const noteLetter = Note.tokenize(note)[0]; // E
const noteIndex = noteLetters.indexOf(noteLetter); // 2 "E is C+2"
const targetNote = noteLetters[(noteIndex + stepNumber - 1) % 8]; // G "G is a third above E"
const rootIndex = notes.indexOf(noteLetter); // 4 "E is 4 semitones above C"
const targetIndex = notes.indexOf(targetNote); // 7 "G is 7 semitones above C"
const indexOffset = targetIndex - rootIndex; // 3 (E to G is normally a 3 semitones)
const stepIndex = steps.indexOf(stepNumber); // 4 ("3" is normally 4 semitones)
const offsetAccidentals = accidentalString(Step.accidentals(step) + Note.accidentals(note) + stepIndex - indexOffset); // "we need to add a # to to the G to make it a major third from E"
return [targetNote, offsetAccidentals].join('');
}
transpose('F#', '3');
transpose('C', '3');
transpose('D', '3');
transpose('E', '3');
transpose('Eb', '3');
transpose('Ebb', '3');
+19
View File
@@ -0,0 +1,19 @@
import { defineConfig } from 'vite';
import { dependencies } from './package.json';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [],
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
},
rollupOptions: {
external: [...Object.keys(dependencies)],
},
target: 'esnext',
},
});
+4 -5
View File
@@ -1,9 +1,11 @@
# @strudel.cycles/tone
Note: This package still works but is no longer maintained in favor of `@strudel.cycles/webaudio`.
This package adds Tone.js functions to strudel Patterns.
## Deprecation Note
This package will not be developed further. Consider using `@strudel.cycles/webaudio` as a replacement.
## Install
```sh
@@ -30,6 +32,3 @@ document.getElementById('play').addEventListener('click', async () => {
Tone.getTransport().start('+0.1');
});
```
[open in codesandbox](https://codesandbox.io/s/strudel-tone-example-5ph2te?file=/src/index.js:0-708)

Some files were not shown because too many files have changed in this diff Show More