Compare commits

..

1 Commits

Author SHA1 Message Date
Felix Roos e99e53ff6d experiment: render audio with web-audio-api-rs 2023-01-11 19:56:36 +01:00
207 changed files with 51522 additions and 25219 deletions
-3
View File
@@ -15,6 +15,3 @@ vite.config.js
!**/*.mjs !**/*.mjs
**/*.tsx **/*.tsx
**/*.ts **/*.ts
**/*.json
**/dev-dist
**/dist
+2 -3
View File
@@ -9,9 +9,8 @@
"ecmaVersion": "latest", "ecmaVersion": "latest",
"sourceType": "module" "sourceType": "module"
}, },
"plugins": ["import"], "plugins": [],
"rules": { "rules": {
"no-unused-vars": ["warn", { "destructuredArrayIgnorePattern": ".", "ignoreRestSiblings": false }], "no-unused-vars": ["warn", { "destructuredArrayIgnorePattern": ".", "ignoreRestSiblings": false }]
"import/no-extraneous-dependencies": ["error", {"devDependencies": true}]
} }
} }
+3 -6
View File
@@ -22,18 +22,15 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }} url: ${{ steps.deployment.outputs.page_url }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: pnpm/action-setup@v2
with:
version: 7
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 18
cache: "pnpm" cache: "npm"
- name: Install Dependencies - name: Install Dependencies
run: pnpm install run: npm ci && cd website && npm ci
- name: Build - name: Build
run: pnpm build run: npm run build
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v2 uses: actions/configure-pages@v2
+5 -8
View File
@@ -11,14 +11,11 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: pnpm/action-setup@v2
with:
version: 7
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
cache: 'pnpm' cache: "npm"
- run: pnpm install - run: npm install
- run: pnpm run format-check - run: npm run format-check
- run: pnpm run lint - run: npm run lint
- run: pnpm test - run: npm test
-2
View File
@@ -38,5 +38,3 @@ talk/public/samples
server/samples/old server/samples/old
repl/stats.html repl/stats.html
coverage coverage
public/icons/apple-splash-*
dev-dist
-3
View File
@@ -7,6 +7,3 @@
packages/mini/krill-parser.js packages/mini/krill-parser.js
packages/xen/tunejs.js packages/xen/tunejs.js
paper paper
pnpm-lock.yaml
pnpm-workspace.yaml
**/dev-dist
+1 -1
View File
@@ -7,7 +7,7 @@
"jsxSingleQuote": false, "jsxSingleQuote": false,
"trailingComma": "all", "trailingComma": "all",
"bracketSpacing": true, "bracketSpacing": true,
"bracketSameLine": false, "jsxBracketSameLine": false,
"arrowParens": "always", "arrowParens": "always",
"proseWrap": "preserve", "proseWrap": "preserve",
"htmlWhitespaceSensitivity": "css", "htmlWhitespaceSensitivity": "css",
+11 -34
View File
@@ -58,22 +58,23 @@ To fix a bug that has been reported,
## Write Tests ## 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. 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 `pnpm test` from the root folder. You can find the tests in each package in the `test` folder. To run all tests, run `npm test` from the root folder.
## Project Setup ## Project Setup
To get the project up and running for development, make sure you have installed: To get the project up and running for development, make sure you have installed:
- [git](https://git-scm.com/) - git
- [node](https://nodejs.org/en/) >= 18 - node, preferably v16
- [pnpm](https://pnpm.io/) (`npm i pnpm -g`)
then, do the following: then, do the following:
```sh ```sh
git clone https://github.com/tidalcycles/strudel.git && cd strudel git clone https://github.com/tidalcycles/strudel.git && cd strudel
pnpm i # install at root to symlink packages npm i # install at root to symlink packages
pnpm start # start repl npx lerna bootstrap # install all dependencies in packages
cd repl && npm i # install repl dependencies
npm run start # start repl
``` ```
Those commands might look slightly different for your OS. Those commands might look slightly different for your OS.
@@ -82,10 +83,6 @@ Please report any problems you've had with the setup instructions!
## Code Style ## Code Style
To make sure the code changes only where it should, we are using prettier to unify the code style. To make sure the code changes only where it should, we are using prettier to unify the code style.
- You can format all files at once by running `pnpm prettier` from the project root
- Run `pnpm format-check` from the project root to check if all files are well formatted
If you use VSCode, you can If you use VSCode, you can
1. install [the prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) 1. install [the prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
@@ -93,29 +90,12 @@ If you use VSCode, you can
3. Choose "Configure Default Formatter..." 3. Choose "Configure Default Formatter..."
4. Select prettier 4. Select prettier
## ESLint
To prevent unwanted runtime errors, this project uses [eslint](https://eslint.org/).
- You can check for lint errors by running `pnpm lint`
There are also eslint extensions / plugins for most editors.
## Running Tests
- Run all tests with `pnpm test`
- Run all tests with UI using `pnpm test-ui`
## Running all CI Checks
When opening a PR, the CI runner will automatically check the code style and eslint, as well as run all tests.
You can run the same check with `pnpm check`
## Package Workflow ## Package Workflow
The project is split into multiple [packages](https://github.com/tidalcycles/strudel/tree/main/packages) with independent versioning. The project is split into multiple [packages](https://github.com/tidalcycles/strudel/tree/main/packages) with independent versioning.
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, When you run `npm i` on the root folder, [npm workspaces](https://docs.npmjs.com/cli/v7/using-npm/workspaces) will symlink all packages
allowing to develop multiple packages at the same time. 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
## Package Publishing ## Package Publishing
@@ -126,15 +106,12 @@ npm login
npx lerna publish 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 ### New Packages
To add a new package, you have to publish it manually the first time, using: To add a new package, you have to publish it manually the first time, using:
```sh ```sh
cd packages/<package-name> && pnpm publish --access public cd packages/<package-name> && npm publish --access public
``` ```
## Have Fun ## Have Fun
-1
View File
@@ -7,7 +7,6 @@ An experiment in making a [Tidal](https://github.com/tidalcycles/tidal/) using w
- Try it here: <https://strudel.tidalcycles.org/> - Try it here: <https://strudel.tidalcycles.org/>
- Docs: <https://strudel.tidalcycles.org/learn/> - Docs: <https://strudel.tidalcycles.org/learn/>
- Technical Blog Post: <https://loophole-letters.vercel.app/strudel> - Technical Blog Post: <https://loophole-letters.vercel.app/strudel>
- 1 Year of Strudel Blog Post: <https://loophole-letters.vercel.app/strudel1year>
## Running Locally ## Running Locally
-17
View File
@@ -1,17 +0,0 @@
// 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';
+1 -3
View File
@@ -2,7 +2,5 @@
"packages": [ "packages": [
"packages/*" "packages/*"
], ],
"version": "independent", "version": "independent"
"npmClient": "pnpm",
"useWorkspaces": true
} }
+10 -49
View File
@@ -3,57 +3,18 @@
This directory can be used to save your own patterns, which then get This directory can be used to save your own patterns, which then get
made into a pattern swatch. made into a pattern swatch.
Example: <https://felixroos.github.io/strudel/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/` !
## deploy ## deploy
### 1. fork the [strudel repo on github](https://github.com/tidalcycles/strudel.git) 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`
### 2. clone your fork to your machine `git clone https://github.com/<your-username>/strudel.git strudel && cd 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`
### 3. create a separate branch like `git branch patternuary && git checkout patternuary` 5. view your patterns at `<your-username>.github.io/strudel/swatch/`
### 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
const site = 'https://<your-username>.github.io';
const 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). 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
+14 -24
View File
@@ -4,30 +4,30 @@
"private": true, "private": true,
"description": "Port of tidalcycles to javascript", "description": "Port of tidalcycles to javascript",
"scripts": { "scripts": {
"setup": "pnpm i",
"pretest": "npm run jsdoc-json", "pretest": "npm run jsdoc-json",
"prebuild": "npm run jsdoc-json", "prebuild": "npm run jsdoc-json",
"prestart": "npm run jsdoc-json", "test": "vitest run --version",
"test": "npm run pretest && vitest run --version", "test-ui": "vitest --ui",
"test-ui": "npm run pretest && vitest --ui", "test-coverage": "vitest --coverage",
"test-coverage": "npm run pretest && vitest --coverage", "bootstrap": "lerna bootstrap",
"snapshot": "npm run pretest && vitest run -u --silent", "setup": "npm i && npm run jsdoc-json && npm run bootstrap && cd website && npm i",
"repl": "npm run prestart && cd website && npm run dev", "snapshot": "vitest run -u --silent",
"start": "npm run prestart && cd website && npm run dev", "repl": "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", "osc": "cd packages/osc && npm run server",
"build": "cd website && npm run build",
"preview": "cd website && npm run preview",
"deploy": "NODE_DEBUG=gh-pages gh-pages -d out", "deploy": "NODE_DEBUG=gh-pages gh-pages -d out",
"jsdoc": "jsdoc packages/ -c jsdoc.config.json", "jsdoc": "jsdoc packages/ -c jsdoc.config.json",
"jsdoc-json": "jsdoc packages/ --template ./node_modules/jsdoc-json --destination doc.json -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", "lint": "eslint . --ext mjs,js --quiet",
"codeformat": "prettier --write .", "codeformat": "prettier --write .",
"format-check": "prettier --check .", "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", "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" "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": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/tidalcycles/strudel.git" "url": "git+https://github.com/tidalcycles/strudel.git"
@@ -45,22 +45,11 @@
"url": "https://github.com/tidalcycles/strudel/issues" "url": "https://github.com/tidalcycles/strudel/issues"
}, },
"homepage": "https://strudel.tidalcycles.org", "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": { "devDependencies": {
"@vitest/ui": "^0.25.7", "@vitest/ui": "^0.25.7",
"c8": "^7.12.0", "c8": "^7.12.0",
"canvas": "^2.11.0", "canvas": "^2.11.0",
"eslint": "^8.28.0", "eslint": "^8.28.0",
"eslint-plugin-import": "^2.27.5",
"events": "^3.3.0", "events": "^3.3.0",
"gh-pages": "^4.0.0", "gh-pages": "^4.0.0",
"jsdoc": "^3.6.10", "jsdoc": "^3.6.10",
@@ -68,6 +57,7 @@
"jsdoc-to-markdown": "^7.1.1", "jsdoc-to-markdown": "^7.1.1",
"lerna": "^4.0.0", "lerna": "^4.0.0",
"prettier": "^2.8.1", "prettier": "^2.8.1",
"rollup-plugin-visualizer": "^5.8.1" "rollup-plugin-visualizer": "^5.8.1",
"vitest": "^0.25.7"
} }
} }
+3 -4
View File
@@ -33,7 +33,6 @@ b: 3/2 - 7/4
c: 7/4 - 2 c: 7/4 - 2
``` ```
- [play with @strudel.cycles/core on codesandbox](https://codesandbox.io/s/strudel-core-test-forked-9ywhv7?file=/src/index.js). - [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/main/packages/core/examples/canvas.html) - [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/main/packages/core/examples/vanilla.html) - [open minimal repl example](https://raw.githack.com/tidalcycles/strudel/package-examples/packages/core/examples/metro.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.fillStyle = clearColor;
ctx.fillRect(0, 0, ww, wh); ctx.fillRect(0, 0, ww, wh);
frame.forEach((f) => { frame.forEach((f) => {
let { x, y, w, h, s, r, angle = 0, fill = 'darkseagreen' } = f.value; let { x, y, w, h, s, r, a = 0, fill = 'darkseagreen' } = f.value;
w *= ww; w *= ww;
h *= wh; h *= wh;
if (r !== undefined && angle !== undefined) { if (r !== undefined && a !== undefined) {
const radians = angle * 2 * Math.PI; const radians = a * 2 * Math.PI;
const [cx, cy] = [(ww - w) / 2, (wh - h) / 2]; const [cx, cy] = [(ww - w) / 2, (wh - h) / 2];
x = cx + Math.cos(radians) * r * cx; x = cx + Math.cos(radians) * r * cx;
y = cy + Math.sin(radians) * r * cy; y = cy + Math.sin(radians) * r * cy;
@@ -51,7 +51,7 @@ Pattern.prototype.animate = function ({ callback, sync = false, smear = 0.5 } =
return silence; return silence;
}; };
export const { x, y, w, h, angle, r, fill, smear } = createParams('x', 'y', 'w', 'h', 'angle', 'r', 'fill', 'smear'); export const { x, y, w, h, a, r, fill, smear } = createParams('x', 'y', 'w', 'h', 'a', 'r', 'fill', 'smear');
export const rescale = register('rescale', function (f, pat) { export const rescale = register('rescale', function (f, pat) {
return pat.mul(x(f).w(f).y(f).h(f)); return pat.mul(x(f).w(f).y(f).h(f));
+72 -92
View File
@@ -4,13 +4,22 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Pattern, sequence, registerControl } from './pattern.mjs'; import { Pattern, sequence } from './pattern.mjs';
const controls = {}; const controls = {};
const generic_params = [ const generic_params = [
/** /**
* Select a sound / sample by name. * Select a sound / sample by name.
* *
* <details style={{display:'none'}}>
* <summary>show all sounds</summary>
*
* 808 (6) 808bd (25) 808cy (25) 808hc (5) 808ht (5) 808lc (5) 808lt (5) 808mc (5) 808mt (5) 808oh (5) 808sd (25) 909 (1) ab (12) ade (10) ades2 (9) ades3 (7) ades4 (6) alex (2) alphabet (26) amencutup (32) armora (7) arp (2) arpy (11) auto (11) baa (7) baa2 (7) bass (4) bass0 (3) bass1 (30) bass2 (5) bass3 (11) bassdm (24) bassfoo (3) battles (2) bd (24) bend (4) bev (2) bin (2) birds (10) birds3 (19) bleep (13) blip (2) blue (2) bottle (13) breaks125 (2) breaks152 (1) breaks157 (1) breaks165 (1) breath (1) bubble (8) can (14) casio (3) cb (1) cc (6) chin (4) circus (3) clak (2) click (4) clubkick (5) co (4) coins (1) control (2) cosmicg (15) cp (2) cr (6) crow (4) d (4) db (13) diphone (38) diphone2 (12) dist (16) dork2 (4) dorkbot (2) dr (42) dr2 (6) dr55 (4) dr_few (8) drum (6) drumtraks (13) e (8) east (9) electro1 (13) em2 (6) erk (1) f (1) feel (7) feelfx (8) fest (1) fire (1) flick (17) fm (17) foo (27) future (17) gab (10) gabba (4) gabbaloud (4) gabbalouder (4) glasstap (3) glitch (8) glitch2 (8) gretsch (24) gtr (3) h (7) hand (17) hardcore (12) hardkick (6) haw (6) hc (6) hh (13) hh27 (13) hit (6) hmm (1) ho (6) hoover (6) house (8) ht (16) if (5) ifdrums (3) incoming (8) industrial (32) insect (3) invaders (18) jazz (8) jungbass (20) jungle (13) juno (12) jvbass (13) kicklinn (1) koy (2) kurt (7) latibro (8) led (1) less (4) lighter (33) linnhats (6) lt (16) made (7) made2 (1) mash (2) mash2 (4) metal (10) miniyeah (4) monsterb (6) moog (7) mouth (15) mp3 (4) msg (9) mt (16) mute (28) newnotes (15) noise (1) noise2 (8) notes (15) numbers (9) oc (4) odx (15) off (1) outdoor (6) pad (3) padlong (1) pebbles (1) perc (6) peri (15) pluck (17) popkick (10) print (11) proc (2) procshort (8) psr (30) rave (8) rave2 (4) ravemono (2) realclaps (4) reverbkick (1) rm (2) rs (1) sax (22) sd (2) seawolf (3) sequential (8) sf (18) sheffield (1) short (5) sid (12) sine (6) sitar (8) sn (52) space (18) speakspell (12) speech (7) speechless (10) speedupdown (9) stab (23) stomp (10) subroc3d (11) sugar (2) sundance (6) tabla (26) tabla2 (46) tablex (3) tacscan (22) tech (13) techno (7) tink (5) tok (4) toys (13) trump (11) ul (10) ulgab (5) uxay (3) v (6) voodoo (5) wind (10) wobble (1) world (3) xmas (1) yeah (31)
*
* <a href="https://tidalcycles.org/docs/configuration/Audio%20Samples/default_library" target="_blank">more info</a>
*
* </details>
*
* @name s * @name s
* @param {string | Pattern} sound The sound / pattern of sounds to pick * @param {string | Pattern} sound The sound / pattern of sounds to pick
* @example * @example
@@ -19,36 +28,20 @@ const generic_params = [
*/ */
['s', 's', 'sound'], ['s', 's', 'sound'],
/** /**
* Selects the given index from the sample map. * The note or sample number to choose for a synth or sampleset
* Numbers too high will wrap around. * Note names currently not working yet, but will hopefully soon. Just stick to numbers for now
* `n` can also be used to play midi numbers, but it is recommended to use `note` instead.
* *
* @name n * @name n
* @param {number | Pattern} value sample index starting from 0 * @param {string | number | Pattern} value note name, note number or sample number
* @example * @example
* s("bd sd,hh*3").n("<0 1>") * s('superpiano').n("<0 1 2 3>").osc()
* @example
* s('superpiano').n("<c4 d4 e4 g4>").osc()
* @example
* n("0 1 2 3").s('east').osc()
*/ */
// also see https://github.com/tidalcycles/strudel/pull/63 // also see https://github.com/tidalcycles/strudel/pull/63
['f', 'n', 'The sample number to choose for a synth or sampleset'], ['f', 'n', 'The note or sample number to choose for a synth or sampleset'],
/**
* Plays the given note name or midi number. A note name consists of
*
* - a letter (a-g or A-G)
* - optional accidentals (b or #)
* - optional octave number (0-9). Defaults to 3
*
* Examples of valid note names: `c`, `bb`, `Bb`, `f#`, `c3`, `A4`, `Eb2`, `c#5`
*
* You can also use midi numbers instead of note names, where 69 is mapped to A4 440Hz in 12EDO.
*
* @name note
* @example
* note("c a f e")
* @example
* note("c4 a4 f4 e4")
* @example
* note("60 69 65 64")
*/
['f', 'note', 'The note or pitch to play a sound or synth with'], ['f', 'note', 'The note or pitch to play a sound or synth with'],
//['s', 'toArg', 'for internal sound routing'], //['s', 'toArg', 'for internal sound routing'],
// ["f", "from", "for internal sound routing"), // ["f", "from", "for internal sound routing"),
@@ -90,7 +83,8 @@ const generic_params = [
*/ */
['f', 'amp', 'like @gain@, but linear.'], ['f', 'amp', 'like @gain@, but linear.'],
/** /**
* Amplitude envelope attack time: Specifies how long it takes for the sound to reach its peak value, relative to the onset. * 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)
* *
* @name attack * @name attack
* @param {number | Pattern} attack time in seconds. * @param {number | Pattern} attack time in seconds.
@@ -98,7 +92,11 @@ const generic_params = [
* note("c3 e3").attack("<0 .1 .5>") * note("c3 e3").attack("<0 .1 .5>")
* *
*/ */
['f', 'attack'], [
'f',
'attack',
'a pattern of numbers to specify the attack time (in seconds) of an envelope applied to each sample.',
],
/** /**
* Select the sound bank to use. To be used together with `s`. The bank name (+ "_") will be prepended to the value of `s`. * Select the sound bank to use. To be used together with `s`. The bank name (+ "_") will be prepended to the value of `s`.
@@ -112,8 +110,8 @@ const generic_params = [
['f', 'bank', 'selects sound bank to use'], ['f', 'bank', 'selects sound bank to use'],
/** /**
* Amplitude envelope decay time: the time it takes after the attack time to reach the sustain level. * Gain 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. * [More info about envelopes](/learn/synths-samples-effects/#envelope)
* *
* @name decay * @name decay
* @param {number | Pattern} time decay time in seconds * @param {number | Pattern} time decay time in seconds
@@ -123,7 +121,7 @@ const generic_params = [
*/ */
['f', 'decay', ''], ['f', 'decay', ''],
/** /**
* Amplitude envelope sustain level: The level which is reached after attack / decay, being sustained until the offset. * Gain envelope sustain level. [More info about envelopes](/learn/synths-samples-effects/#envelope)
* *
* @name sustain * @name sustain
* @param {number | Pattern} gain sustain level between 0 and 1 * @param {number | Pattern} gain sustain level between 0 and 1
@@ -133,7 +131,7 @@ const generic_params = [
*/ */
['f', 'sustain', ''], ['f', 'sustain', ''],
/** /**
* Amplitude envelope release time: The time it takes after the offset to go from sustain level to zero. * Gain envelope release time. [More info about envelopes](/learn/synths-samples-effects/#envelope)
* *
* @name release * @name release
* @param {number | Pattern} time release time in seconds * @param {number | Pattern} time release time in seconds
@@ -153,30 +151,30 @@ const generic_params = [
], ],
// TODO: in tidal, it seems to be normalized // TODO: in tidal, it seems to be normalized
/** /**
* Sets the center frequency of the **b**and-**p**ass **f**ilter. * Sets the center frequency of the band-pass filter.
* *
* @name bpf * @name bandf
* @param {number | Pattern} frequency center frequency * @param {number | Pattern} frequency center frequency
* @synonyms bandf * @synonyms bpf
* @example * @example
* s("bd sd,hh*3").bpf("<1000 2000 4000 8000>") * s("bd sd,hh*3").bandf("<1000 2000 4000 8000>")
* *
*/ */
['f', 'bpf', ''],
['f', 'bandf', 'A pattern of numbers from 0 to 1. Sets the center frequency of the band-pass filter.'], ['f', 'bandf', 'A pattern of numbers from 0 to 1. Sets the center frequency of the band-pass filter.'],
['f', 'bpf', ''],
// TODO: in tidal, it seems to be normalized // TODO: in tidal, it seems to be normalized
/** /**
* Sets the **b**and-**p**ass **q**-factor (resonance) * Sets the q-factor of the band-pass filter
* *
* @name bpq * @name bandq
* @param {number | Pattern} q q factor * @param {number | Pattern} q q factor
* @synonyms bandq * @synonyms bpq
* @example * @example
* s("bd sd").bpf(500).bpq("<0 1 2 3>") * s("bd sd").bandf(500).bandq("<0 1 2 3>")
* *
*/ */
['f', 'bpq', ''],
['f', 'bandq', 'a pattern of anumbers from 0 to 1. Sets the q-factor of the band-pass filter.'], ['f', 'bandq', 'a pattern of anumbers from 0 to 1. Sets the q-factor of the band-pass filter.'],
['f', 'bpq', ''],
/** /**
* 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. * 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.
* *
@@ -284,49 +282,50 @@ 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.', '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 **l**ow-**p**ass **f**ilter. * Applies the cutoff frequency of the low-pass filter.
* *
* @name lpf * @name cutoff
* @param {number | Pattern} frequency audible between 0 and 20000 * @param {number | Pattern} frequency audible between 0 and 20000
* @synonyms cutoff * @synonyms lpf
* @example * @example
* s("bd sd,hh*3").lpf("<4000 2000 1000 500 200 100>") * s("bd sd,hh*3").cutoff("<4000 2000 1000 500 200 100>")
* *
*/ */
['f', 'lpf'],
['f', 'cutoff', 'a pattern of numbers from 0 to 1. Applies the cutoff frequency of the low-pass filter.'], ['f', 'cutoff', 'a pattern of numbers from 0 to 1. Applies the cutoff frequency of the low-pass filter.'],
['f', 'lpf'],
/** /**
* Applies the cutoff frequency of the **h**igh-**p**ass **f**ilter. * Applies the cutoff frequency of the high-pass filter.
* *
* @name hpf * @name hcutoff
* @param {number | Pattern} frequency audible between 0 and 20000 * @param {number | Pattern} frequency audible between 0 and 20000
* @synonyms hcutoff * @synonyms hpf
* @example * @example
* s("bd sd,hh*4").hpf("<4000 2000 1000 500 200 100>") * s("bd sd,hh*4").hcutoff("<4000 2000 1000 500 200 100>")
* *
*/ */
['f', 'hpf', ''],
['f', 'hcutoff', ''], ['f', 'hcutoff', ''],
['f', 'hpf', ''],
/** /**
* Controls the **h**igh-**p**ass **q**-value. * Applies the resonance of the high-pass filter.
* *
* @name hpq * @name hresonance
* @param {number | Pattern} q resonance factor between 0 and 50 * @param {number | Pattern} q resonance factor between 0 and 50
* @synonyms hresonance * @synonyms hpq
* @example * @example
* s("bd sd,hh*4").hpf(2000).hpq("<0 10 20 30>") * s("bd sd,hh*4").hcutoff(2000).hresonance("<0 10 20 30>")
* *
*/ */
['f', 'hresonance', ''],
['f', 'hpq', ''], ['f', 'hpq', ''],
['f', 'hresonance', ''],
// TODO: add hpq synonym
/** /**
* Controls the **l**ow-**p**ass **q**-value. * Applies the cutoff frequency of the low-pass filter.
* *
* @name lpq * @name resonance
* @param {number | Pattern} q resonance factor between 0 and 50 * @param {number | Pattern} q resonance factor between 0 and 50
* @synonyms resonance * @synonyms lpq
* @example * @example
* s("bd sd,hh*4").lpf(2000).lpq("<0 10 20 30>") * s("bd sd,hh*4").cutoff(2000).resonance("<0 10 20 30>")
* *
*/ */
['f', 'lpq'], ['f', 'lpq'],
@@ -393,7 +392,6 @@ const generic_params = [
* *
* @name detune * @name detune
* @param {number | Pattern} amount between 0 and 1 * @param {number | Pattern} amount between 0 and 1
* @superdirtOnly
* @example * @example
* n("0 3 7").s('superzow').octave(3).detune("<0 .25 .5 1 2>").osc() * n("0 3 7").s('superzow').octave(3).detune("<0 .25 .5 1 2>").osc()
* *
@@ -406,7 +404,6 @@ const generic_params = [
* @param {number | Pattern} dry 0 = wet, 1 = dry * @param {number | Pattern} dry 0 = wet, 1 = dry
* @example * @example
* n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc() * n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc()
* @superdirtOnly
* *
*/ */
[ [
@@ -465,7 +462,6 @@ const generic_params = [
* @param {number | Pattern} wet between 0 and 1 * @param {number | Pattern} wet between 0 and 1
* @example * @example
* n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc() * n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc()
* @superdirtOnly
* *
*/ */
['f', 'leslie', ''], ['f', 'leslie', ''],
@@ -476,7 +472,6 @@ const generic_params = [
* @param {number | Pattern} rate 6.7 for fast, 0.7 for slow * @param {number | Pattern} rate 6.7 for fast, 0.7 for slow
* @example * @example
* n("0,4,7").s("supersquare").leslie(1).lrate("<1 2 4 8>").osc() * n("0,4,7").s("supersquare").leslie(1).lrate("<1 2 4 8>").osc()
* @superdirtOnly
* *
*/ */
// TODO: the rate seems to "lag" (in the example, 1 will be fast) // TODO: the rate seems to "lag" (in the example, 1 will be fast)
@@ -488,7 +483,6 @@ const generic_params = [
* @param {number | Pattern} meters somewhere between 0 and 1 * @param {number | Pattern} meters somewhere between 0 and 1
* @example * @example
* n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc() * n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc()
* @superdirtOnly
* *
*/ */
['f', 'lsize', ''], ['f', 'lsize', ''],
@@ -524,7 +518,6 @@ const generic_params = [
* @param {number | Pattern} octave octave number * @param {number | Pattern} octave octave number
* @example * @example
* n("0,4,7").s('supersquare').octave("<3 4 5 6>").osc() * n("0,4,7").s('supersquare').octave("<3 4 5 6>").osc()
* @superDirtOnly
*/ */
['i', 'octave', ''], ['i', 'octave', ''],
['f', 'offset', ''], // TODO: what is this? not found in tidal doc ['f', 'offset', ''], // TODO: what is this? not found in tidal doc
@@ -628,15 +621,14 @@ const generic_params = [
/** /**
* Sets the room size of the reverb, see {@link room}. * Sets the room size of the reverb, see {@link room}.
* *
* @name roomsize * @name size
* @synonyms size
* @param {number | Pattern} size between 0 and 10 * @param {number | Pattern} size between 0 and 10
* @example * @example
* s("bd sd").room(.8).roomsize("<0 1 2 4 8>") * s("bd sd").room(.8).size("<0 1 2 4 8>")
* *
*/ */
// TODO: find out why : // TODO: find out why :
// s("bd sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc() // s("bd sd").room(.8).size("<0 .2 .4 .6 .8 [1,0]>").osc()
// .. does not work. Is it because room is only one effect? // .. does not work. Is it because room is only one effect?
[ [
'f', 'f',
@@ -689,7 +681,6 @@ const generic_params = [
* @param {number | string | Pattern} unit see description above * @param {number | string | Pattern} unit see description above
* @example * @example
* speed("1 2 .5 3").s("bd").unit("c").osc() * speed("1 2 .5 3").s("bd").unit("c").osc()
* @superdirtOnly
* *
*/ */
[ [
@@ -706,7 +697,6 @@ const generic_params = [
* @param {number | Pattern} squiz Try passing multiples of 2 to it - 2, 4, 8 etc. * @param {number | Pattern} squiz Try passing multiples of 2 to it - 2, 4, 8 etc.
* @example * @example
* squiz("2 4/2 6 [8 16]").s("bd").osc() * squiz("2 4/2 6 [8 16]").s("bd").osc()
* @superdirtOnly
* *
*/ */
['f', 'squiz', ''], ['f', 'squiz', ''],
@@ -813,41 +803,31 @@ const generic_params = [
['f', 'uid', ''], ['f', 'uid', ''],
['f', 'val', ''], ['f', 'val', ''],
['f', 'cps', ''], ['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', ''], ['f', 'clip', ''],
]; ];
// TODO: slice / splice https://www.youtube.com/watch?v=hKhPdO0RKDQ&list=PL2lW1zNIIwj3bDkh-Y3LUGDuRcoUigoDs&index=13 // TODO: slice / splice https://www.youtube.com/watch?v=hKhPdO0RKDQ&list=PL2lW1zNIIwj3bDkh-Y3LUGDuRcoUigoDs&index=13
const makeControl = function (name) { const _name = (name, ...pats) => sequence(...pats).withValue((x) => ({ [name]: x }));
const func = (...pats) => sequence(...pats).withValue((x) => ({ [name]: x }));
const setter = function (...pats) { const _setter = (func, name) =>
function (...pats) {
if (!pats.length) { if (!pats.length) {
return this.fmap((value) => ({ [name]: value })); return this.fmap((value) => ({ [name]: value }));
} }
return this.set(func(...pats)); return this.set(func(...pats));
}; };
Pattern.prototype[name] = setter;
registerControl(name, func);
return func;
};
generic_params.forEach(([type, name, description]) => { generic_params.forEach(([type, name, description]) => {
controls[name] = makeControl(name); controls[name] = (...pats) => _name(name, ...pats);
Pattern.prototype[name] = _setter(controls[name], name);
}); });
// create custom param // create custom param
controls.createParam = (name) => { controls.createParam = (name) => {
return makeControl(name); const func = (...pats) => _name(name, ...pats);
Pattern.prototype[name] = _setter(func, name);
return (...pats) => _name(name, ...pats);
}; };
controls.createParams = (...names) => controls.createParams = (...names) =>
+6 -3
View File
@@ -8,10 +8,13 @@ import createClock from './zyklus.mjs';
import { logger } from './logger.mjs'; import { logger } from './logger.mjs';
export class Cyclist { export class Cyclist {
worker;
pattern;
started = false;
cps = 1; // TODO
getTime;
phase = 0;
constructor({ interval, onTrigger, onToggle, onError, getTime, latency = 0.1 }) { constructor({ interval, onTrigger, onToggle, onError, getTime, latency = 0.1 }) {
this.started = false;
this.cps = 1; // TODO
this.phase = 0;
this.getTime = getTime; this.getTime = getTime;
this.onToggle = onToggle; this.onToggle = onToggle;
this.latency = latency; this.latency = latency;
-6
View File
@@ -59,9 +59,3 @@ export const cleanupDraw = (clearScreen = true) => {
clearInterval(window.strudelScheduler); 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))); return Math.min(ons, offs) <= 1 ? [n, x] : _bjork(...(ons > offs ? left(n, x) : right(n, x)));
}; };
export const bjork = function (ons, steps) { const bjork = function (ons, steps) {
const offs = steps - ons; const offs = steps - ons;
const x = Array(ons).fill([1]); const x = Array(ons).fill([1]);
const y = Array(offs).fill([0]); const y = Array(offs).fill([0]);
+2 -2
View File
@@ -1,13 +1,13 @@
<input <input
type="text" type="text"
id="text" id="text"
value="seq('a', ['b', 'c'])" value="cat('a', 'b')"
style="width: 100%; font-size: 2em; outline: none; margin-bottom: 10px" style="width: 100%; font-size: 2em; outline: none; margin-bottom: 10px"
spellcheck="false" spellcheck="false"
/> />
<div id="output"></div> <div id="output"></div>
<script type="module"> <script type="module">
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8'); const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.0.2');
Object.assign(window, strudel); // assign all strudel functions to global scope to use with eval Object.assign(window, strudel); // assign all strudel functions to global scope to use with eval
const input = document.getElementById('text'); const input = document.getElementById('text');
const getEvents = () => { const getEvents = () => {
+2 -2
View File
@@ -2,13 +2,13 @@
<input <input
type="text" type="text"
id="text" id="text"
value="seq('tomato', 'indigo', ['white', 'steelblue']).fast(4)" value="cat('orange', 'indigo')"
style="width: 100%; font-size: 2em; background: black; color: white; outline: none; position: absolute; top: 0" style="width: 100%; font-size: 2em; background: black; color: white; outline: none; position: absolute; top: 0"
spellcheck="false" spellcheck="false"
/> />
<canvas id="canvas"></canvas> <canvas id="canvas"></canvas>
<script type="module"> <script type="module">
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8'); const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.0.2');
// this adds all strudel functions to the global scope, to be used by eval // this adds all strudel functions to the global scope, to be used by eval
Object.assign(window, strudel); Object.assign(window, strudel);
// setup elements // setup elements
+90
View File
@@ -0,0 +1,90 @@
<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
@@ -0,0 +1,44 @@
<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>
+29 -16
View File
@@ -6,36 +6,46 @@
<title>Buildless Vanilla Strudel REPL</title> <title>Buildless Vanilla Strudel REPL</title>
</head> </head>
<body style="margin: 0; background: #222"> <body style="margin: 0; background: #222">
<div style="display: grid; height: 100vh; grid-template-rows: 32px auto"> <div style="display: grid; height: 100vh">
<button id="start" style="width: 100vw; height: 32px">evaluate</button>
<textarea <textarea
id="text" id="text"
style="font-size: 2em; border: 0; color: white; background: transparent; outline: none; padding: 20px" style="font-size: 2em; border: 0; color: white; background: transparent; outline: none; padding: 20px"
spellcheck="false" spellcheck="false"
></textarea> ></textarea>
</div> </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> <div id="output"></div>
<script type="module"> <script type="module">
import { controls, repl, evalScope } from 'https://cdn.skypack.dev/@strudel.cycles/core@0.6.8'; 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.6.0'; import { mini } from 'https://cdn.skypack.dev/@strudel.cycles/mini@0.3.2';
import { transpiler } from 'https://cdn.skypack.dev/@strudel.cycles/transpiler@0.6.0'; import { transpiler } from 'https://cdn.skypack.dev/@strudel.cycles/transpiler@0.3.2';
import { import { getAudioContext, webaudioOutput } from 'https://cdn.skypack.dev/@strudel.cycles/webaudio@0.3.3';
getAudioContext,
webaudioOutput,
initAudioOnFirstClick,
} from 'https://cdn.skypack.dev/@strudel.cycles/webaudio@0.6.0';
initAudioOnFirstClick();
const ctx = getAudioContext(); const ctx = getAudioContext();
const input = document.getElementById('text'); const input = document.getElementById('text');
input.innerHTML = getTune(); input.innerHTML = getTune();
evalScope( evalScope(
controls, controls,
import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8'), import('https://cdn.skypack.dev/@strudel.cycles/core@0.3.2'),
import('https://cdn.skypack.dev/@strudel.cycles/mini@0.6.0'), import('https://cdn.skypack.dev/@strudel.cycles/mini@0.3.2'),
import('https://cdn.skypack.dev/@strudel.cycles/tonal@0.6.0'), import('https://cdn.skypack.dev/@strudel.cycles/tonal@0.3.3'),
import('https://cdn.skypack.dev/@strudel.cycles/webaudio@0.6.0'), import('https://cdn.skypack.dev/@strudel.cycles/webaudio@0.3.3'),
); );
const { evaluate } = repl({ const { evaluate } = repl({
@@ -43,7 +53,10 @@
getTime: () => ctx.currentTime, getTime: () => ctx.currentTime,
transpiler, transpiler,
}); });
document.getElementById('start').addEventListener('click', () => evaluate(input.value)); document.getElementById('start').addEventListener('click', () => {
ctx.resume();
evaluate(input.value);
});
function getTune() { function getTune() {
return `await samples('github:tidalcycles/Dirt-Samples/master') return `await samples('github:tidalcycles/Dirt-Samples/master')
@@ -1,12 +1,12 @@
import { controls, repl, evalScope } from '@strudel.cycles/core'; import { controls, repl, evalScope, setStringParser } from '@strudel.cycles/core';
import { getAudioContext, webaudioOutput, initAudioOnFirstClick } from '@strudel.cycles/webaudio'; import { mini } from '@strudel.cycles/mini';
import { transpiler } from '@strudel.cycles/transpiler'; import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio';
// import { transpiler } from '@strudel.cycles/transpiler';
import tune from './tune.mjs'; import tune from './tune.mjs';
const ctx = getAudioContext(); const ctx = getAudioContext();
const input = document.getElementById('text'); const input = document.getElementById('text');
input.innerHTML = tune; input.innerHTML = tune;
initAudioOnFirstClick();
evalScope( evalScope(
controls, controls,
@@ -16,13 +16,14 @@ evalScope(
import('@strudel.cycles/tonal'), import('@strudel.cycles/tonal'),
); );
setStringParser(mini);
const { evaluate } = repl({ const { evaluate } = repl({
defaultOutput: webaudioOutput, defaultOutput: webaudioOutput,
getTime: () => ctx.currentTime, getTime: () => ctx.currentTime,
transpiler, // transpiler,
}); });
document.getElementById('start').addEventListener('click', () => { document.getElementById('start').addEventListener('click', () => {
ctx.resume(); ctx.resume();
console.log('eval', input.value);
evaluate(input.value); evaluate(input.value);
}); });
+885
View File
@@ -0,0 +1,885 @@
{
"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,12 +11,5 @@
}, },
"devDependencies": { "devDependencies": {
"vite": "^3.2.0" "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( stack(
// amen // amen
@@ -29,3 +29,11 @@ stack(
, ,
n("0 1").s("east").delay(.5).degradeBy(.8).speed(rand.range(.5,1.5)) n("0 1").s("east").delay(.5).degradeBy(.8).speed(rand.range(.5,1.5))
).reset("<x@7 x(5,8)>")`; ).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
@@ -0,0 +1,65 @@
<!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
+3 -13
View File
@@ -1,17 +1,11 @@
{ {
"name": "@strudel.cycles/core", "name": "@strudel.cycles/core",
"version": "0.6.8", "version": "0.5.0",
"description": "Port of Tidal Cycles to JavaScript", "description": "Port of Tidal Cycles to JavaScript",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": { "scripts": {
"test": "vitest run", "test": "vitest run"
"build": "vite build",
"prepublishOnly": "pnpm build"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -33,9 +27,5 @@
"dependencies": { "dependencies": {
"fraction.js": "^4.2.0" "fraction.js": "^4.2.0"
}, },
"gitHead": "0e26d4e741500f5bae35b023608f062a794905c2", "gitHead": "0e26d4e741500f5bae35b023608f062a794905c2"
"devDependencies": {
"vite": "^3.2.2",
"vitest": "^0.25.7"
}
} }
+131 -551
View File
File diff suppressed because it is too large Load Diff
-12
View File
@@ -283,15 +283,3 @@ export function pianoroll({
ctx.stroke(); ctx.stroke();
return this; 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, afterEval,
getTime, getTime,
transpiler, transpiler,
onToggle,
editPattern, editPattern,
onToggle,
}) { }) {
const scheduler = new Cyclist({ const scheduler = new Cyclist({
interval, interval,
@@ -35,7 +35,7 @@ export function repl({
getTime, getTime,
onToggle, onToggle,
}); });
setTime(() => scheduler.now()); // TODO: refactor? setTime(() => scheduler.getPhase()); // TODO: refactor?
const evaluate = async (code, autostart = true) => { const evaluate = async (code, autostart = true) => {
if (!code) { if (!code) {
throw new Error('no code to evaluate'); throw new Error('no code to evaluate');
-8
View File
@@ -114,14 +114,6 @@ 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. * A continuous pattern of random numbers, between 0 and 1.
* *
+3 -48
View File
@@ -44,9 +44,6 @@ import {
ply, ply,
rev, rev,
time, time,
run,
hitch,
set,
} from '../index.mjs'; } from '../index.mjs';
import { steady } from '../signal.mjs'; import { steady } from '../signal.mjs';
@@ -206,7 +203,7 @@ describe('Pattern', () => {
), ),
); );
}); });
it('can squeezeout() structure', () => { it('can SqueezeOut() structure', () => {
sameFirst( sameFirst(
sequence(1, [2, 3]).add.squeezeout(10, 20, 30), sequence(1, [2, 3]).add.squeezeout(10, 20, 30),
sequence([11, [12, 13]], [21, [22, 23]], [31, [32, 33]]), sequence([11, [12, 13]], [21, [22, 23]], [31, [32, 33]]),
@@ -254,7 +251,7 @@ describe('Pattern', () => {
), ),
); );
}); });
it('can squeezeout() structure', () => { it('can SqueezeOut() structure', () => {
sameFirst(sequence(1, [2, 3]).keep.squeezeout(10, 20, 30), sequence([1, [2, 3]], [1, [2, 3]], [1, [2, 3]])); sameFirst(sequence(1, [2, 3]).keep.squeezeout(10, 20, 30), sequence([1, [2, 3]], [1, [2, 3]], [1, [2, 3]]));
}); });
}); });
@@ -296,7 +293,7 @@ describe('Pattern', () => {
), ),
); );
}); });
it('can squeezeout() structure', () => { it('can SqueezeOut() structure', () => {
sameFirst(sequence(1, [2, 3]).keepif.squeezeout(true, true, false), sequence([1, [2, 3]], [1, [2, 3]], silence)); sameFirst(sequence(1, [2, 3]).keepif.squeezeout(true, true, false), sequence([1, [2, 3]], [1, [2, 3]], silence));
}); });
}); });
@@ -911,18 +908,6 @@ 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', () => { describe('linger', () => {
it('Can linger on the first quarter of a cycle', () => { 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( expect(sequence(0, 1, 2, 3, 4, 5, 6, 7).linger(0.25).firstCycle()).toStrictEqual(
@@ -931,14 +916,6 @@ describe('Pattern', () => {
}); });
}); });
describe('alignments', () => { describe('alignments', () => {
it('Can combine controls', () => {
sameFirst(s('bd').set.in.n(3), s('bd').n(3));
sameFirst(s('bd').set.squeeze.n(3, 4), sequence(s('bd').n(3), s('bd').n(4)));
});
it('Can combine functions with alignmed controls', () => {
sameFirst(s('bd').apply(fast(2).set(n(3))), s('bd').fast(2).set.in.n(3));
sameFirst(s('bd').apply(fast(2).set.in.n(3)), s('bd').fast(2).set.in.n(3));
});
it('Can squeeze arguments', () => { it('Can squeeze arguments', () => {
expect(sequence(1, 2).add.squeeze(4, 5).firstCycle()).toStrictEqual(sequence(5, 6, 6, 7).firstCycle()); expect(sequence(1, 2).add.squeeze(4, 5).firstCycle()).toStrictEqual(sequence(5, 6, 6, 7).firstCycle());
}); });
@@ -959,26 +936,4 @@ describe('Pattern', () => {
expect(stack(sequence('a', silence), pure('a').mask(0, 1)).defragmentHaps().firstCycle().length).toStrictEqual(2); 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));
});
});
describe('composable functions', () => {
it('Can compose functions', () => {
sameFirst(sequence(3, 4).fast(2).rev().fast(2), fast(2).rev().fast(2)(sequence(3, 4)));
});
it('Can compose by method chaining operators with controls', () => {
sameFirst(s('bd').apply(set.n(3).fast(2)), s('bd').set.n(3).fast(2));
});
it('Can compose by method chaining operators and alignments with controls', () => {
sameFirst(s('bd').apply(set.in.n(3).fast(2)), s('bd').set.n(3).fast(2));
// sameFirst(s('bd').apply(set.squeeze.n(3).fast(2)), s('bd').set.squeeze.n(3).fast(2));
});
});
}); });
+1 -1
View File
@@ -26,7 +26,7 @@ export const backgroundImage = function (src, animateOptions = {}) {
({ ({
style: () => (container.style = bg + ';' + value), style: () => (container.style = bg + ';' + value),
className: () => (container.className = value + ' ' + initialClassName), className: () => (container.className = value + ' ' + initialClassName),
})[option](); }[option]());
}; };
const funcOptions = Object.entries(animateOptions).filter(([_, v]) => typeof v === 'function'); const funcOptions = Object.entries(animateOptions).filter(([_, v]) => typeof v === 'function');
const stringOptions = Object.entries(animateOptions).filter(([_, v]) => typeof v === 'string'); const stringOptions = Object.entries(animateOptions).filter(([_, v]) => typeof v === 'string');
+1 -3
View File
@@ -58,7 +58,6 @@ export const valueToMidi = (value, fallbackValue) => {
/** /**
* @deprecated does not appear to be referenced or invoked anywhere in the codebase * @deprecated does not appear to be referenced or invoked anywhere in the codebase
* @noAutocomplete
*/ */
export const getFreq = (noteOrMidi) => { export const getFreq = (noteOrMidi) => {
if (typeof noteOrMidi === 'number') { if (typeof noteOrMidi === 'number') {
@@ -69,7 +68,6 @@ export const getFreq = (noteOrMidi) => {
/** /**
* @deprecated does not appear to be referenced or invoked anywhere in the codebase * @deprecated does not appear to be referenced or invoked anywhere in the codebase
* @noAutocomplete
*/ */
export const midi2note = (n) => { export const midi2note = (n) => {
const oct = Math.floor(n / 12) - 1; const oct = Math.floor(n / 12) - 1;
@@ -139,7 +137,7 @@ export const removeUndefineds = (xs) => xs.filter((x) => x != undefined);
export const flatten = (arr) => [].concat(...arr); export const flatten = (arr) => [].concat(...arr);
export const id = (a) => a; export const id = (a) => a;
export const constant = curry((a, b) => a); export const constant = (a, b) => a;
export const listRange = (min, max) => Array.from({ length: max - min + 1 }, (_, i) => i + min); export const listRange = (min, max) => Array.from({ length: max - min + 1 }, (_, i) => i + min);
-19
View File
@@ -1,19 +0,0 @@
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,7 +92,6 @@ async function load() {
['message'].forEach((k) => _csound.on(k, (...args) => eventLogger(k, args))); ['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('-m0d'); // see -m flag https://csound.com/docs/manual/CommandFlags.html
await _csound.setOption('--sample-accurate'); await _csound.setOption('--sample-accurate');
await _csound.setOption('-odac');
await _csound.compileCsdText(csd); await _csound.compileCsdText(csd);
// await _csound.compileOrc(livecodeOrc); // await _csound.compileOrc(livecodeOrc);
await _csound.compileOrc(presetsOrc); await _csound.compileOrc(presetsOrc);
+4 -14
View File
@@ -1,15 +1,10 @@
{ {
"name": "@strudel.cycles/csound", "name": "@strudel.cycles/csound",
"version": "0.6.2", "version": "0.5.1",
"description": "csound bindings for strudel", "description": "csound bindings for strudel",
"main": "index.mjs", "main": "csound.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": { "scripts": {
"build": "vite build", "test": "echo \"No tests present.\" && exit 0"
"prepublishOnly": "npm run build"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -32,11 +27,6 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@csound/browser": "6.18.5", "@csound/browser": "^6.18.3"
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*"
},
"devDependencies": {
"vite": "^3.2.2"
} }
} }
-19
View File
@@ -1,19 +0,0 @@
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 -2
View File
@@ -30,5 +30,4 @@ note(`[[e5 [b4 c5] d5 [c5 b4]]
</strudel-repl> </strudel-repl>
``` ```
- Note that the Code is placed inside HTML comments to prevent the browser from treating it as HTML. Note that the Code is placed inside HTML comments to prevent the browser from treating it as HTML.
- [Play with this example on stackblitz](https://stackblitz.com/edit/js-75cvww?file=index.html)
-4
View File
@@ -3,10 +3,6 @@
This package contains the strudel code transformer and evaluator. 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. 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 ## Install
```sh ```sh
+297
View File
@@ -0,0 +1,297 @@
{
"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=="
}
}
}
+5 -16
View File
@@ -1,21 +1,15 @@
{ {
"name": "@strudel.cycles/eval", "name": "@strudel.cycles/eval",
"version": "0.6.2", "version": "0.5.0",
"description": "Code evaluator for strudel", "description": "Code evaluator for strudel",
"main": "index.mjs", "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", "type": "module",
"directories": { "directories": {
"test": "test" "test": "test"
}, },
"scripts": {
"test": "vitest run"
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/tidalcycles/strudel.git" "url": "git+https://github.com/tidalcycles/strudel.git"
@@ -34,17 +28,12 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*", "@strudel.cycles/core": "^0.5.0",
"estraverse": "^5.3.0", "estraverse": "^5.3.0",
"shift-ast": "^7.0.0", "shift-ast": "^7.0.0",
"shift-codegen": "^8.1.0", "shift-codegen": "^8.1.0",
"shift-parser": "^8.0.0", "shift-parser": "^8.0.0",
"shift-spec": "^2019.0.0", "shift-spec": "^2019.0.0",
"shift-traverser": "^1.0.0" "shift-traverser": "^1.0.0"
},
"devDependencies": {
"@strudel.cycles/mini": "workspace:*",
"vite": "^3.2.2",
"vitest": "^0.25.7"
} }
} }
-19
View File
@@ -1,19 +0,0 @@
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,3 +7,7 @@ This package adds midi functionality to strudel Patterns.
```sh ```sh
npm i @strudel.cycles/midi --save 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
@@ -0,0 +1,130 @@
{
"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=="
}
}
}
+3 -14
View File
@@ -1,16 +1,8 @@
{ {
"name": "@strudel.cycles/midi", "name": "@strudel.cycles/midi",
"version": "0.6.0", "version": "0.5.0",
"description": "Midi API for strudel", "description": "Midi API for strudel",
"main": "index.mjs", "main": "index.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": {
"build": "vite build",
"prepublishOnly": "npm run build"
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/tidalcycles/strudel.git" "url": "git+https://github.com/tidalcycles/strudel.git"
@@ -29,11 +21,8 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*", "@strudel.cycles/tone": "^0.5.0",
"@strudel.cycles/webaudio": "workspace:*", "tone": "^14.7.77",
"webmidi": "^3.0.21" "webmidi": "^3.0.21"
},
"devDependencies": {
"vite": "^3.2.2"
} }
} }
-19
View File
@@ -1,19 +0,0 @@
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 To generate the parser, run
```js ```js
npm build:parser npm run build:parser
``` ```
+1031
View File
File diff suppressed because it is too large Load Diff
+6 -12
View File
@@ -1,18 +1,12 @@
{ {
"name": "@strudel.cycles/mini", "name": "@strudel.cycles/mini",
"version": "0.6.0", "version": "0.5.0",
"description": "Mini notation for strudel", "description": "Mini notation for strudel",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": { "scripts": {
"test": "vitest run", "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": { "repository": {
"type": "git", "type": "git",
@@ -32,11 +26,11 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*" "@strudel.cycles/core": "^0.5.0",
"@strudel.cycles/eval": "^0.5.0",
"@strudel.cycles/tone": "^0.5.0"
}, },
"devDependencies": { "devDependencies": {
"peggy": "^2.0.1", "peggy": "^2.0.1"
"vite": "^3.2.2",
"vitest": "^0.25.7"
} }
} }
-19
View File
@@ -1,19 +0,0 @@
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() s("<bd sd> hh").osc()
``` ```
or just [click here](https://strudel.tidalcycles.org/#cygiPGJkIHNkPiBoaCIpLm9zYygp)... or just [click here](http://localhost:3000/#cygiPGJkIHNkPiBoaCIpLm9zYygp)...
You can read more about [how to use Superdirt with Strudel the Tutorial](https://strudel.tidalcycles.org/learn/input-output/#superdirt-api) You can read more about [how to use Superdirt with Strudel the Tutorial](https://strudel.tidalcycles.org/learn/outputs#superdirt-api)
-1
View File
@@ -39,7 +39,6 @@ let startedAt = -1;
/** /**
* *
* Sends each hap as an OSC message, which can be picked up by SuperCollider or any other OSC-enabled software. * Sends each hap as an OSC message, which can be picked up by SuperCollider or any other OSC-enabled software.
* For more info, read [MIDI & OSC in the docs](https://strudel.tidalcycles.org/learn/input-output)
* *
* @name osc * @name osc
* @memberof Pattern * @memberof Pattern
+60
View File
@@ -0,0 +1,60 @@
{
"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": {}
}
}
}
+4 -11
View File
@@ -1,19 +1,14 @@
{ {
"name": "@strudel.cycles/osc", "name": "@strudel.cycles/osc",
"version": "0.6.0", "version": "0.4.0",
"description": "OSC messaging for strudel", "description": "OSC messaging for strudel",
"main": "osc.mjs", "main": "osc.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": { "scripts": {
"test": "echo \"No tests present.\" && exit 0",
"server": "node server.js", "server": "node server.js",
"tidal-sniffer": "node tidal-sniffer.js", "tidal-sniffer": "node tidal-sniffer.js",
"client": "npx serve -p 4321", "client": "npx serve -p 4321",
"build-bin": "npx pkg server.js --targets node16-macos-x64,node16-win-x64,node16-linux-x64 --out-path bin", "build": "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": { "repository": {
"type": "git", "type": "git",
@@ -36,11 +31,9 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*",
"osc-js": "^2.4.0" "osc-js": "^2.4.0"
}, },
"devDependencies": { "devDependencies": {
"pkg": "^5.7.0", "pkg": "^5.7.0"
"vite": "^3.2.2"
} }
} }
-19
View File
@@ -1,19 +0,0 @@
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,6 +11,8 @@ node_modules
dist-ssr dist-ssr
*.local *.local
!dist
# Editor directories and files # Editor directories and files
.vscode/* .vscode/*
!.vscode/extensions.json !.vscode/extensions.json
+8 -1
View File
@@ -33,4 +33,11 @@ export function Repl({ tune }) {
} }
``` ```
For a more sophisticated example, check out the [nano-repl](./examples/nano-repl/)! ## 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
```
File diff suppressed because one or more lines are too long
+401
View File
@@ -0,0 +1,401 @@
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
@@ -0,0 +1 @@
.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* lerna-debug.log*
node_modules node_modules
dist !dist
dist-ssr dist-ssr
*.local *.local
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
.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
@@ -0,0 +1 @@
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
@@ -0,0 +1 @@
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
@@ -0,0 +1,14 @@
<!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
+3 -10
View File
@@ -1,7 +1,7 @@
{ {
"name": "@strudel.cycles/nano-repl", "name": "nano-repl",
"private": true, "private": true,
"version": "0.6.0", "version": "0.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@@ -10,14 +10,7 @@
}, },
"dependencies": { "dependencies": {
"react": "^18.2.0", "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": { "devDependencies": {
"@types/react": "^18.0.17", "@types/react": "^18.0.17",
+17 -9
View File
@@ -1,21 +1,27 @@
import { controls, evalScope } from '@strudel.cycles/core'; import { evalScope, controls } from '@strudel.cycles/core';
import { CodeMirror, useHighlighting, useKeydown, useStrudel, flash } from '@strudel.cycles/react'; import { getAudioContext, panic, webaudioOutput } from '@strudel.cycles/webaudio';
import { getAudioContext, initAudioOnFirstClick, panic, webaudioOutput } from '@strudel.cycles/webaudio';
import { useCallback, useState } from 'react'; 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 './style.css';
// import { prebake } from '../../../../../repl/src/prebake.mjs'; // import { prebake } from '../../../../../repl/src/prebake.mjs';
initAudioOnFirstClick();
// TODO: only import stuff when play is pressed? // TODO: only import stuff when play is pressed?
evalScope( evalScope(
controls, controls,
import('@strudel.cycles/core'), 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/tonal'),
import('@strudel.cycles/mini'), import('@strudel.cycles/mini'),
import('@strudel.cycles/xen'), import('@strudel.cycles/xen'),
import('@strudel.cycles/webaudio'), import('@strudel.cycles/webaudio'),
import('@strudel.cycles/osc'), import('@strudel.cycles/osc'),
import('@strudel.cycles/webdirt'),
import('@strudel.cycles/serial'),
import('@strudel.cycles/soundfonts'),
); );
const defaultTune = `samples({ const defaultTune = `samples({
@@ -24,7 +30,7 @@ const defaultTune = `samples({
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'], hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
}, 'github:tidalcycles/Dirt-Samples/master/'); }, 'github:tidalcycles/Dirt-Samples/master/');
stack( stack(
s("bd,[~ <sd!3 sd(3,4,2)>],hh*8") // drums s("bd,[~ <sd!3 sd(3,4,2)>],hh(3,4)") // drums
.speed(perlin.range(.7,.9)) // random sample speed variation .speed(perlin.range(.7,.9)) // random sample speed variation
//.hush() //.hush()
,"<a1 b1*2 a1(3,8) e2>" // bassline ,"<a1 b1*2 a1(3,8) e2>" // bassline
@@ -37,7 +43,7 @@ stack(
.gain(.4) // turn down .gain(.4) // turn down
.cutoff(sine.slow(7).range(300,5000)) // automate cutoff .cutoff(sine.slow(7).range(300,5000)) // automate cutoff
//.hush() //.hush()
,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings('lefthand') // chords ,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings() // chords
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice .superimpose(x=>x.add(.04)) // add second, slightly detuned voice
.add(perlin.range(0,.5)) // random pitch variation .add(perlin.range(0,.5)) // random pitch variation
.n() // wrap in "n" .n() // wrap in "n"
@@ -97,7 +103,7 @@ function App() {
scheduler.start(); scheduler.start();
} }
} else if (e.code === 'Period') { } else if (e.code === 'Period') {
scheduler.stop(); scheduler.pause();
panic(); panic();
e.preventDefault(); e.preventDefault();
} }
@@ -108,11 +114,13 @@ function App() {
); );
return ( return (
<div> <div>
<nav className="z-[12] w-full flex justify-center fixed bottom-0"> {/* <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">
<div className="bg-slate-500 space-x-2 px-2 rounded-t-md"> <div className="bg-slate-500 space-x-2 px-2 rounded-t-md">
<button <button
onClick={async () => { onClick={async () => {
await evaluate(code); await evaluate(code);
await getAudioContext().resume();
scheduler.start(); scheduler.start();
}} }}
> >
+17 -14
View File
@@ -1,18 +1,24 @@
{ {
"name": "@strudel.cycles/react", "name": "@strudel.cycles/react",
"version": "0.6.0", "version": "0.5.0",
"description": "React components for strudel", "description": "React components for strudel",
"main": "src/index.js", "main": "dist/index.cjs.js",
"publishConfig": { "module": "dist/index.es.js",
"main": "dist/index.js", "exports": {
"module": "dist/index.mjs" ".": {
"require": "./dist/index.cjs.js",
"import": "./dist/index.es.js"
},
"./dist/style.css": "./dist/style.css"
}, },
"files": [
"dist"
],
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
"watch": "vite build --watch", "watch": "vite build --watch",
"preview": "vite preview", "preview": "vite preview"
"prepublishOnly": "npm run build"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -32,14 +38,11 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@codemirror/autocomplete": "^6.4.0",
"@codemirror/lang-javascript": "^6.1.1", "@codemirror/lang-javascript": "^6.1.1",
"@codemirror/state": "^6.2.0", "@strudel.cycles/core": "^0.5.0",
"@codemirror/view": "^6.7.3", "@strudel.cycles/tone": "^0.5.0",
"@lezer/highlight": "^1.1.3", "@strudel.cycles/transpiler": "^0.5.0",
"@strudel.cycles/core": "workspace:*", "@strudel.cycles/webaudio": "^0.5.0",
"@strudel.cycles/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"@uiw/codemirror-themes": "^4.12.4", "@uiw/codemirror-themes": "^4.12.4",
"@uiw/react-codemirror": "^4.12.4", "@uiw/react-codemirror": "^4.12.4",
"react-hook-inview": "^4.5.0" "react-hook-inview": "^4.5.0"
+1
View File
@@ -6,6 +6,7 @@ import { controls, evalScope } from '@strudel.cycles/core';
evalScope( evalScope(
controls, controls,
import('@strudel.cycles/core'), import('@strudel.cycles/core'),
// import('@strudel.cycles/tone'),
import('@strudel.cycles/tonal'), import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'), import('@strudel.cycles/mini'),
import('@strudel.cycles/midi'), import('@strudel.cycles/midi'),
@@ -1,77 +0,0 @@
import { createRoot } from 'react-dom/client';
import jsdoc from '../../../../doc.json';
const getDocLabel = (doc) => doc.name || doc.longname;
const getInnerText = (html) => {
var div = document.createElement('div');
div.innerHTML = html;
return div.textContent || div.innerText || '';
};
export function Autocomplete({ doc }) {
return (
<div className="prose dark:prose-invert max-h-[400px] overflow-auto">
<h3 className="pt-0 mt-0">{getDocLabel(doc)}</h3>
<div dangerouslySetInnerHTML={{ __html: doc.description }} />
<ul>
{doc.params?.map(({ name, type, description }, i) => (
<li key={i}>
{name} : {type.names?.join(' | ')} {description ? <> - {getInnerText(description)}</> : ''}
</li>
))}
</ul>
<div>
{doc.examples?.map((example, i) => (
<div key={i}>
<pre
className="cursor-pointer"
onMouseDown={(e) => {
console.log('ola!');
navigator.clipboard.writeText(example);
e.stopPropagation();
}}
>
{example}
</pre>
</div>
))}
</div>
</div>
);
}
const jsdocCompletions = jsdoc.docs
.filter(
(doc) =>
getDocLabel(doc) &&
!getDocLabel(doc).startsWith('_') &&
!['package'].includes(doc.kind) &&
!['superdirtOnly', 'noAutocomplete'].some((tag) => doc.tags?.find((t) => t.originalTitle === tag)),
)
// https://codemirror.net/docs/ref/#autocomplete.Completion
.map((doc) /*: Completion */ => ({
label: getDocLabel(doc),
// detail: 'xxx', // An optional short piece of information to show (with a different style) after the label.
info: () => {
const node = document.createElement('div');
// if Autocomplete is non-interactive, it could also be rendered at build time..
// .. using renderToStaticMarkup
createRoot(node).render(<Autocomplete doc={doc} />);
return node;
},
type: 'function', // https://codemirror.net/docs/ref/#autocomplete.Completion.type
}));
export const strudelAutocomplete = (context /* : CompletionContext */) => {
let word = context.matchBefore(/\w*/);
if (word.from == word.to && !context.explicit) return null;
return {
from: word.from,
options: jsdocCompletions,
/* options: [
{ label: 'match', type: 'keyword' },
{ label: 'hello', type: 'variable', info: '(World)' },
{ label: 'magic', type: 'text', apply: '⠁⭒*.✩.*⭒⠁', detail: 'macro' },
], */
};
};
+4 -23
View File
@@ -6,8 +6,6 @@ import { javascript } from '@codemirror/lang-javascript';
import strudelTheme from '../themes/strudel-theme'; import strudelTheme from '../themes/strudel-theme';
import './style.css'; import './style.css';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { autocompletion } from '@codemirror/autocomplete';
import { strudelAutocomplete } from './Autocomplete';
export const setFlash = StateEffect.define(); export const setFlash = StateEffect.define();
const flashField = StateField.define({ const flashField = StateField.define({
@@ -51,12 +49,11 @@ const highlightField = StateField.define({
try { try {
for (let e of tr.effects) { for (let e of tr.effects) {
if (e.is(setHighlights)) { if (e.is(setHighlights)) {
const { haps } = e.value;
const marks = const marks =
haps e.value
.map((hap) => .map((hap) =>
(hap.context.locations || []).map(({ start, end }) => { (hap.context.locations || []).map(({ start, end }) => {
const color = hap.context.color || e.value.color || '#FFCA28'; const color = hap.context.color || '#FFCA28';
let from = tr.newDoc.line(start.line).from + start.column; let from = tr.newDoc.line(start.line).from + start.column;
let to = tr.newDoc.line(end.line).from + end.column; let to = tr.newDoc.line(end.line).from + end.column;
const l = tr.newDoc.length; const l = tr.newDoc.length;
@@ -82,24 +79,9 @@ const highlightField = StateField.define({
provide: (f) => EditorView.decorations.from(f), provide: (f) => EditorView.decorations.from(f),
}); });
const extensions = [ const extensions = [javascript(), strudelTheme, highlightField, flashField];
javascript(),
highlightField,
flashField,
// javascriptLanguage.data.of({ autocomplete: strudelAutocomplete }),
// autocompletion({ override: [strudelAutocomplete] }),
autocompletion({ override: [] }), // wait for https://github.com/uiwjs/react-codemirror/pull/458
];
export default function CodeMirror({ export default function CodeMirror({ value, onChange, onViewChanged, onSelectionChange, options, editorDidMount }) {
value,
onChange,
onViewChanged,
onSelectionChange,
theme,
options,
editorDidMount,
}) {
const handleOnChange = useCallback( const handleOnChange = useCallback(
(value) => { (value) => {
onChange?.(value); onChange?.(value);
@@ -124,7 +106,6 @@ export default function CodeMirror({
<> <>
<_CodeMirror <_CodeMirror
value={value} value={value}
theme={theme || strudelTheme}
onChange={handleOnChange} onChange={handleOnChange}
onCreateEditor={handleOnCreateEditor} onCreateEditor={handleOnCreateEditor}
onUpdate={handleOnUpdate} onUpdate={handleOnUpdate}
+1 -8
View File
@@ -2,7 +2,7 @@ import React from 'react';
export function Icon({ type }) { export function Icon({ type }) {
return ( return (
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" className="sc-h-5 sc-w-5" viewBox="0 0 20 20" fill="currentColor">
{ {
{ {
refresh: ( refresh: (
@@ -26,13 +26,6 @@ export function Icon({ type }) {
clipRule="evenodd" 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] }[type]
} }
</svg> </svg>
+35 -50
View File
@@ -1,36 +1,21 @@
import { pianoroll } from '@strudel.cycles/core';
import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio'; import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio';
import React, { useLayoutEffect, useMemo, useRef, useState, useCallback, useEffect } from 'react'; import React, { useLayoutEffect, useMemo, useRef, useState, useCallback, useEffect } from 'react';
import { useInView } from 'react-hook-inview'; import { useInView } from 'react-hook-inview';
import 'tailwindcss/tailwind.css'; import 'tailwindcss/tailwind.css';
import cx from '../cx'; import cx from '../cx';
import useHighlighting from '../hooks/useHighlighting.mjs'; import useHighlighting from '../hooks/useHighlighting.mjs';
import usePatternFrame from '../hooks/usePatternFrame.mjs';
import useStrudel from '../hooks/useStrudel.mjs'; import useStrudel from '../hooks/useStrudel.mjs';
import CodeMirror6, { flash } from './CodeMirror6'; import CodeMirror6, { flash } from './CodeMirror6';
import { Icon } from './Icon'; import { Icon } from './Icon';
import styles from './MiniRepl.module.css'; import styles from './MiniRepl.module.css';
import './style.css'; import './style.css';
import { logger } from '@strudel.cycles/core'; import { logger } from '@strudel.cycles/core';
import useEvent from '../hooks/useEvent.mjs';
import useKeydown from '../hooks/useKeydown.mjs';
const getTime = () => getAudioContext().currentTime; const getTime = () => getAudioContext().currentTime;
export function MiniRepl({ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, withCanvas = false, canvasHeight = 200 }) {
tune,
hideOutsideView = false,
enableKeyboard,
drawTime,
punchcard,
canvasHeight = 200,
theme,
highlightColor,
}) {
drawTime = drawTime || (punchcard ? [0, 4] : undefined);
const evalOnMount = !!drawTime;
const drawContext = useCallback(
!!drawTime ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null,
[drawTime],
);
const { const {
code, code,
setCode, setCode,
@@ -49,13 +34,25 @@ export function MiniRepl({
} = useStrudel({ } = useStrudel({
initialCode: tune, initialCode: tune,
defaultOutput: webaudioOutput, defaultOutput: webaudioOutput,
editPattern: (pat) => (punchcard ? pat.punchcard() : pat),
getTime, getTime,
evalOnMount, editPattern: (pat, id) => {
drawContext, return pat.withContext((ctx) => ({ ...ctx, id }));
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 [view, setView] = useState();
const [ref, isVisible] = useInView({ const [ref, isVisible] = useInView({
threshold: 0.01, threshold: 0.01,
@@ -71,31 +68,9 @@ export function MiniRepl({
view, view,
pattern, pattern,
active: started && !activeCode?.includes('strudel disable-highlighting'), active: started && !activeCode?.includes('strudel disable-highlighting'),
getTime: () => scheduler.now(), getTime: () => scheduler.getPhase(),
color: highlightColor,
}); });
// keyboard shortcuts
useKeydown(
useCallback(
async (e) => {
if (view?.hasFocus) {
if (e.ctrlKey || e.altKey) {
if (e.code === 'Enter') {
e.preventDefault();
flash(view);
await activateCode();
} else if (e.code === 'Period') {
stop();
e.preventDefault();
}
}
}
},
[activateCode, stop, view],
),
);
// set active pattern on ctrl+enter // set active pattern on ctrl+enter
useLayoutEffect(() => { useLayoutEffect(() => {
if (enableKeyboard) { if (enableKeyboard) {
@@ -134,8 +109,8 @@ export function MiniRepl({
<div className={styles.container} ref={ref}> <div className={styles.container} ref={ref}>
<div className={styles.header}> <div className={styles.header}>
<div className={styles.buttons}> <div className={styles.buttons}>
<button className={cx(styles.button, started ? 'animate-pulse' : '')} onClick={() => togglePlay()}> <button className={cx(styles.button, started ? 'sc-animate-pulse' : '')} onClick={() => togglePlay()}>
<Icon type={started ? 'stop' : 'play'} /> <Icon type={started ? 'pause' : 'play'} />
</button> </button>
<button className={cx(isDirty ? styles.button : styles.buttonDisabled)} onClick={() => activateCode()}> <button className={cx(isDirty ? styles.button : styles.buttonDisabled)} onClick={() => activateCode()}>
<Icon type="refresh" /> <Icon type="refresh" />
@@ -144,9 +119,9 @@ export function MiniRepl({
{error && <div className={styles.error}>{error.message}</div>} {error && <div className={styles.error}>{error.message}</div>}
</div> </div>
<div className={styles.body}> <div className={styles.body}>
{show && <CodeMirror6 value={code} onChange={setCode} onViewChanged={setView} theme={theme} />} {show && <CodeMirror6 value={code} onChange={setCode} onViewChanged={setView} />}
</div> </div>
{drawTime && ( {withCanvas && (
<canvas <canvas
id={canvasId} id={canvasId}
className="w-full pointer-events-none" className="w-full pointer-events-none"
@@ -159,7 +134,7 @@ export function MiniRepl({
></canvas> ></canvas>
)} )}
{!!log.length && ( {!!log.length && (
<div className="bg-gray-800 rounded-md p-2"> <div className="sc-bg-gray-800 sc-rounded-md sc-p-2">
{log.map(({ message }, i) => ( {log.map(({ message }, i) => (
<div key={i}>{message}</div> <div key={i}>{message}</div>
))} ))}
@@ -173,3 +148,13 @@ export function MiniRepl({
function useLogger(onTrigger) { function useLogger(onTrigger) {
useEvent(logger.key, onTrigger); useEvent(logger.key, onTrigger);
} }
// TODO: dedupe
function useEvent(name, onTrigger, useCapture = false) {
useEffect(() => {
document.addEventListener(name, onTrigger, useCapture);
return () => {
document.removeEventListener(name, onTrigger, useCapture);
};
}, [onTrigger]);
}
@@ -1,27 +1,27 @@
.container { .container {
@apply overflow-hidden; @apply sc-rounded-md sc-overflow-hidden sc-bg-[#222222];
} }
.header { .header {
@apply flex justify-between bg-lineHighlight border-t border-l border-r border-lineHighlight rounded-t-md overflow-hidden; @apply sc-flex sc-justify-between sc-bg-slate-700 sc-border-t sc-border-slate-500;
} }
.buttons { .buttons {
@apply flex; @apply sc-flex;
} }
.button { .button {
@apply cursor-pointer w-16 flex items-center justify-center p-1 border-r border-lineHighlight text-foreground hover:bg-background; @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;
} }
.buttonDisabled { .buttonDisabled {
@apply w-16 flex items-center justify-center p-1 opacity-50 cursor-not-allowed border-r border-lineHighlight; @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;
} }
.error { .error {
@apply text-right p-1 text-sm text-red-200; @apply sc-text-right sc-p-1 sc-text-sm sc-text-red-200;
} }
.body { .body {
@apply overflow-auto relative; @apply sc-overflow-auto sc-relative;
} }
+4 -4
View File
@@ -5,10 +5,10 @@
font-size: 18px; font-size: 18px;
} }
.cm-theme {
width: 100%;
}
.cm-theme-light { .cm-theme-light {
width: 100%; width: 100%;
} }
.cm-line > * {
background: #00000095;
}
-12
View File
@@ -1,12 +0,0 @@
import { useEffect } from 'react';
function useEvent(name, onTrigger, useCapture = false) {
useEffect(() => {
document.addEventListener(name, onTrigger, useCapture);
return () => {
document.removeEventListener(name, onTrigger, useCapture);
};
}, [onTrigger]);
}
export default useEvent;
+7 -8
View File
@@ -1,27 +1,26 @@
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { setHighlights } from '../components/CodeMirror6'; import { setHighlights } from '../components/CodeMirror6';
function useHighlighting({ view, pattern, active, getTime, color }) { function useHighlighting({ view, pattern, active, getTime }) {
const highlights = useRef([]); const highlights = useRef([]);
const lastEnd = useRef(0); const lastEnd = useRef();
useEffect(() => { useEffect(() => {
if (view) { if (view) {
if (pattern && active) { if (pattern && active) {
lastEnd.current = 0;
let frame = requestAnimationFrame(function updateHighlights() { let frame = requestAnimationFrame(function updateHighlights() {
try { try {
const audioTime = getTime(); const audioTime = getTime();
// force min framerate of 10 fps => fixes crash on tab refocus, where lastEnd could be far away // 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 // see https://github.com/tidalcycles/strudel/issues/108
const begin = Math.max(lastEnd.current ?? audioTime, audioTime - 1 / 10, -0.01); // negative time seems buggy const begin = Math.max(lastEnd.current || audioTime, audioTime - 1 / 10, 0); // negative time seems buggy
const span = [begin, audioTime + 1 / 60]; const span = [begin, audioTime + 1 / 60];
lastEnd.current = span[1]; lastEnd.current = span[1];
highlights.current = highlights.current.filter((hap) => hap.whole.end > audioTime); // keep only highlights that are still active highlights.current = highlights.current.filter((hap) => hap.whole.end > audioTime); // keep only highlights that are still active
const haps = pattern.queryArc(...span).filter((hap) => hap.hasOnset()); const haps = pattern.queryArc(...span).filter((hap) => hap.hasOnset());
highlights.current = highlights.current.concat(haps); // add potential new onsets highlights.current = highlights.current.concat(haps); // add potential new onsets
view.dispatch({ effects: setHighlights.of({ haps: highlights.current, color }) }); // highlight all still active + new active haps view.dispatch({ effects: setHighlights.of(highlights.current) }); // highlight all still active + new active haps
} catch (err) { } catch (err) {
view.dispatch({ effects: setHighlights.of({ haps: [] }) }); view.dispatch({ effects: setHighlights.of([]) });
} }
frame = requestAnimationFrame(updateHighlights); frame = requestAnimationFrame(updateHighlights);
}); });
@@ -30,10 +29,10 @@ function useHighlighting({ view, pattern, active, getTime, color }) {
}; };
} else { } else {
highlights.current = []; highlights.current = [];
view.dispatch({ effects: setHighlights.of({ haps: [] }) }); view.dispatch({ effects: setHighlights.of([]) });
} }
} }
}, [pattern, active, view, color]); }, [pattern, active, view]);
} }
export default useHighlighting; export default useHighlighting;
+5 -19
View File
@@ -2,32 +2,23 @@ import { useCallback, useEffect, useRef } from 'react';
import 'tailwindcss/tailwind.css'; import 'tailwindcss/tailwind.css';
import useFrame from '../hooks/useFrame.mjs'; import useFrame from '../hooks/useFrame.mjs';
function usePatternFrame({ pattern, started, getTime, onDraw, drawTime = [-2, 2] }) { function usePatternFrame({ pattern, started, getTime, onDraw }) {
let [lookbehind, lookahead] = drawTime;
lookbehind = Math.abs(lookbehind);
let visibleHaps = useRef([]); let visibleHaps = useRef([]);
let lastFrame = useRef(null); 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( const { start: startFrame, stop: stopFrame } = useFrame(
useCallback(() => { useCallback(() => {
const phase = getTime() + lookahead; const phase = getTime();
if (lastFrame.current === null) { if (lastFrame.current === null) {
lastFrame.current = phase; lastFrame.current = phase;
return; return;
} }
const haps = pattern.queryArc(Math.max(lastFrame.current, phase - 1 / 10), phase); const haps = pattern.queryArc(Math.max(lastFrame.current, phase - 1 / 10), phase);
const cycles = 4;
lastFrame.current = phase; lastFrame.current = phase;
visibleHaps.current = (visibleHaps.current || []) visibleHaps.current = (visibleHaps.current || [])
.filter((h) => h.whole.end >= phase - lookbehind - lookahead) // in frame .filter((h) => h.whole.end > phase - cycles) // in frame
.concat(haps.filter((h) => h.hasOnset())); .concat(haps.filter((h) => h.hasOnset()));
onDraw(pattern, phase - lookahead, visibleHaps.current, drawTime); onDraw(phase, visibleHaps.current);
}, [pattern]), }, [pattern]),
); );
useEffect(() => { useEffect(() => {
@@ -38,11 +29,6 @@ function usePatternFrame({ pattern, started, getTime, onDraw, drawTime = [-2, 2]
stopFrame(); stopFrame();
} }
}, [started]); }, [started]);
return {
clear: () => {
visibleHaps.current = [];
},
};
} }
export default usePatternFrame; export default usePatternFrame;
+5 -42
View File
@@ -1,7 +1,6 @@
import { useRef, useCallback, useEffect, useMemo, useState } from 'react'; import { useRef, useCallback, useEffect, useMemo, useState } from 'react';
import { repl } from '@strudel.cycles/core'; import { repl } from '@strudel.cycles/core';
import { transpiler } from '@strudel.cycles/transpiler'; import { transpiler } from '@strudel.cycles/transpiler';
import usePatternFrame from './usePatternFrame';
import usePostMessage from './usePostMessage.mjs'; import usePostMessage from './usePostMessage.mjs';
function useStrudel({ function useStrudel({
@@ -17,8 +16,6 @@ function useStrudel({
onEvalError, onEvalError,
onToggle, onToggle,
canvasId, canvasId,
drawContext,
drawTime = [-2, 2],
}) { }) {
const id = useMemo(() => s4(), []); const id = useMemo(() => s4(), []);
canvasId = canvasId || `canvas-${id}`; canvasId = canvasId || `canvas-${id}`;
@@ -30,7 +27,6 @@ function useStrudel({
const [pattern, setPattern] = useState(); const [pattern, setPattern] = useState();
const [started, setStarted] = useState(false); const [started, setStarted] = useState(false);
const isDirty = code !== activeCode; const isDirty = code !== activeCode;
const shouldPaint = useCallback((pat) => !!(pat?.context?.onPaint && drawContext), [drawContext]);
// TODO: make sure this hook reruns when scheduler.started changes // TODO: make sure this hook reruns when scheduler.started changes
const { scheduler, evaluate, start, stop, pause } = useMemo( const { scheduler, evaluate, start, stop, pause } = useMemo(
@@ -44,13 +40,12 @@ function useStrudel({
onEvalError?.(err); onEvalError?.(err);
}, },
getTime, getTime,
drawContext,
transpiler, transpiler,
editPattern,
beforeEval: ({ code }) => { beforeEval: ({ code }) => {
setCode(code); setCode(code);
beforeEval?.(); beforeEval?.();
}, },
editPattern: editPattern ? (pat) => editPattern(pat, id) : undefined,
afterEval: ({ pattern: _pattern, code }) => { afterEval: ({ pattern: _pattern, code }) => {
setActiveCode(code); setActiveCode(code);
setPattern(_pattern); setPattern(_pattern);
@@ -76,41 +71,19 @@ function useStrudel({
}); });
const activateCode = useCallback( const activateCode = useCallback(
async (autostart = true) => { async (autostart = true) => {
const res = await evaluate(code, autostart); await evaluate(code, autostart);
broadcast({ type: 'start', from: id }); broadcast({ type: 'start', from: id });
return res;
}, },
[evaluate, code], [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(); const inited = useRef();
useEffect(() => { useEffect(() => {
if (!inited.current && evalOnMount && code) { if (!inited.current && evalOnMount && code) {
inited.current = true; inited.current = true;
evaluate(code, false).then((pat) => drawFirstFrame(pat)); activateCode();
} }
}, [evalOnMount, code, evaluate, drawFirstFrame]); }, [activateCode, evalOnMount, code]);
// this will stop the scheduler when hot reloading in development // this will stop the scheduler when hot reloading in development
useEffect(() => { useEffect(() => {
@@ -121,22 +94,12 @@ function useStrudel({
const togglePlay = async () => { const togglePlay = async () => {
if (started) { if (started) {
scheduler.stop(); scheduler.pause();
drawFirstFrame(pattern);
} else { } else {
await activateCode(); await activateCode();
} }
}; };
const error = schedulerError || evalError; const error = schedulerError || evalError;
usePatternFrame({
pattern,
started: shouldPaint(pattern) && started,
getTime: () => scheduler.now(),
drawTime,
onDraw,
});
return { return {
id, id,
canvasId, canvasId,
+4 -6
View File
@@ -1,11 +1,9 @@
// import 'tailwindcss/tailwind.css'; // import 'tailwindcss/tailwind.css';
export { default as CodeMirror, flash } from './components/CodeMirror6'; // !SSR export { default as CodeMirror, flash } from './components/CodeMirror6';
export * from './components/MiniRepl'; // !SSR export * from './components/MiniRepl';
export { default as useHighlighting } from './hooks/useHighlighting'; // !SSR export { default as useHighlighting } from './hooks/useHighlighting';
export { default as useStrudel } from './hooks/useStrudel'; // !SSR
export { default as usePostMessage } from './hooks/usePostMessage'; export { default as usePostMessage } from './hooks/usePostMessage';
export { default as useStrudel } from './hooks/useStrudel';
export { default as useKeydown } from './hooks/useKeydown'; export { default as useKeydown } from './hooks/useKeydown';
export { default as useEvent } from './hooks/useEvent';
export { default as strudelTheme } from './themes/strudel-theme';
export { default as cx } from './cx'; export { default as cx } from './cx';
+1
View File
@@ -7,4 +7,5 @@ module.exports = {
corePlugins: { corePlugins: {
preflight: false, preflight: false,
}, },
prefix: 'sc-',
}; };
+1 -1
View File
@@ -14,7 +14,7 @@ export default defineConfig({
lib: { lib: {
entry: resolve(__dirname, 'src', 'index.js'), entry: resolve(__dirname, 'src', 'index.js'),
formats: ['es', 'cjs'], formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]), fileName: (ext) => `index.${ext}.js`,
// for UMD name: 'GlobalName' // for UMD name: 'GlobalName'
}, },
rollupOptions: { rollupOptions: {
+1
View File
@@ -1,3 +1,4 @@
# @strudel.cycles/serial # @strudel.cycles/serial
This package adds webserial functionality to strudel Patterns, for e.g. sending messages to arduino microcontrollers. This package adds webserial functionality to strudel Patterns, for e.g. sending messages to arduino microcontrollers.
+2 -16
View File
@@ -1,16 +1,8 @@
{ {
"name": "@strudel.cycles/serial", "name": "@strudel.cycles/serial",
"version": "0.6.0", "version": "0.3.0",
"description": "Webserial API for strudel", "description": "Webserial API for strudel",
"main": "serial.mjs", "main": "serial.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": {
"build": "vite build",
"prepublishOnly": "npm run build"
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/tidalcycles/strudel.git" "url": "git+https://github.com/tidalcycles/strudel.git"
@@ -27,11 +19,5 @@
"bugs": { "bugs": {
"url": "https://github.com/tidalcycles/strudel/issues" "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
@@ -1,19 +0,0 @@
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
@@ -0,0 +1,301 @@
{
"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=="
}
}
}

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