diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000..580f7338 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,46 @@ +name: Build and Deploy + +on: [workflow_dispatch] + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + deployments: write + +# Allow one concurrent deployment +concurrency: + group: "pages" + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v3 + with: + node-version: 16 + cache: "npm" + - name: Install Dependencies + run: npm ci && cd repl && npm ci && cd ../tutorial && npm ci + + - name: Build + run: npm run build + + - name: Setup Pages + uses: actions/configure-pages@v2 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + # Upload entire repository + path: "./out" + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 diff --git a/.gitignore b/.gitignore index 43684564..c390b0e5 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,5 @@ tutorial.rendered.mdx doc.json talk/public/EmuSP12 talk/public/samples -server/samples/old \ No newline at end of file +server/samples/old +repl/stats.html \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index e8635e12..e005426f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,8 @@ "cSpell.words": [ "subspan", "vals" - ] + ], + "yaml.schemas": { + "https://json.schemastore.org/github-workflow.json": "file:///home/felix/projects/strudel/.github/workflows/deploy.yml" + } } \ No newline at end of file diff --git a/dependencies.svg b/dependencies.svg new file mode 100644 index 00000000..2d2fc61d --- /dev/null +++ b/dependencies.svg @@ -0,0 +1,1806 @@ + + + + + + +dependency-cruiser output + + +cluster_packages + +packages + + +cluster_packages/core + +core + + +cluster_packages/core/test + +test + + +cluster_packages/embed + +embed + + +cluster_packages/eval + +eval + + +cluster_packages/eval/shift-traverser + +shift-traverser + + +cluster_packages/eval/test + +test + + +cluster_packages/midi + +midi + + +cluster_packages/mini + +mini + + +cluster_packages/mini/test + +test + + +cluster_packages/osc + +osc + + +cluster_packages/react + +react + + +cluster_packages/react/dist + +dist + + +cluster_packages/react/src + +src + + +cluster_packages/react/src/components + +components + + +cluster_packages/react/src/hooks + +hooks + + +cluster_packages/react/src/themes + +themes + + +cluster_packages/serial + +serial + + +cluster_packages/soundfonts + +soundfonts + + +cluster_packages/tonal + +tonal + + +cluster_packages/tonal/test + +test + + +cluster_packages/tone + +tone + + +cluster_packages/tone/test + +test + + +cluster_packages/webaudio + +webaudio + + +cluster_packages/webdirt + +webdirt + + +cluster_packages/xen + +xen + + +cluster_packages/xen/test + +test + + + +packages/core/controls.mjs + + +controls.mjs + + + + + +packages/core/pattern.mjs + + +pattern.mjs + + + + + +packages/core/controls.mjs->packages/core/pattern.mjs + + + + + +packages/core/drawLine.mjs + + +drawLine.mjs + + + + + +packages/core/pattern.mjs->packages/core/drawLine.mjs + + + + + +packages/core/fraction.mjs + + +fraction.mjs + + + + + +packages/core/pattern.mjs->packages/core/fraction.mjs + + + + + +packages/core/util.mjs + + +util.mjs + + + + + +packages/core/pattern.mjs->packages/core/util.mjs + + + + + +packages/core/timespan.mjs + + +timespan.mjs + + + + + +packages/core/pattern.mjs->packages/core/timespan.mjs + + + + + +packages/core/hap.mjs + + +hap.mjs + + + + + +packages/core/pattern.mjs->packages/core/hap.mjs + + + + + +packages/core/state.mjs + + +state.mjs + + + + + +packages/core/pattern.mjs->packages/core/state.mjs + + + + + +packages/core/value.mjs + + +value.mjs + + + + + +packages/core/pattern.mjs->packages/core/value.mjs + + + + + +packages/core/drawLine.mjs->packages/core/fraction.mjs + + + + + +packages/core/fraction.mjs->packages/core/timespan.mjs + + + + + + + +packages/core/euclid.mjs + + +euclid.mjs + + + + + +packages/core/euclid.mjs->packages/core/pattern.mjs + + + + + +packages/core/euclid.mjs->packages/core/fraction.mjs + + + + + +packages/core/euclid.mjs->packages/core/util.mjs + + + + + +packages/core/timespan.mjs->packages/core/fraction.mjs + + + + + + + +packages/core/gist.js + + +gist.js + + + + + +packages/core/index.mjs + + +index.mjs + + + + + +packages/core/index.mjs->packages/core/controls.mjs + + + + + +packages/core/index.mjs->packages/core/pattern.mjs + + + + + +packages/core/index.mjs->packages/core/fraction.mjs + + + + + +packages/core/index.mjs->packages/core/euclid.mjs + + + + + +packages/core/index.mjs->packages/core/util.mjs + + + + + +packages/core/index.mjs->packages/core/timespan.mjs + + + + + +packages/core/index.mjs->packages/core/gist.js + + + + + +packages/core/index.mjs->packages/core/hap.mjs + + + + + +packages/core/signal.mjs + + +signal.mjs + + + + + +packages/core/index.mjs->packages/core/signal.mjs + + + + + +packages/core/speak.mjs + + +speak.mjs + + + + + +packages/core/index.mjs->packages/core/speak.mjs + + + + + + + +packages/core/index.mjs->packages/core/state.mjs + + + + + +packages/core/signal.mjs->packages/core/pattern.mjs + + + + + +packages/core/signal.mjs->packages/core/fraction.mjs + + + + + +packages/core/signal.mjs->packages/core/util.mjs + + + + + +packages/core/signal.mjs->packages/core/hap.mjs + + + + + +packages/core/speak.mjs->packages/core/index.mjs + + + + + + + +packages/core/value.mjs->packages/core/util.mjs + + + + + +packages/core/test/drawLine.test.mjs + + +drawLine.test.mjs + + + + + +packages/core/test/drawLine.test.mjs->packages/core/pattern.mjs + + + + + +packages/core/test/drawLine.test.mjs->packages/core/drawLine.mjs + + + + + +packages/core/test/fraction.test.mjs + + +fraction.test.mjs + + + + + +packages/core/test/fraction.test.mjs->packages/core/fraction.mjs + + + + + +packages/core/test/pattern.test.mjs + + +pattern.test.mjs + + + + + +packages/core/test/pattern.test.mjs->packages/core/index.mjs + + + + + +packages/core/test/pattern.test.mjs->packages/core/signal.mjs + + + + + +packages/core/test/util.test.mjs + + +util.test.mjs + + + + + +packages/core/test/util.test.mjs->packages/core/pattern.mjs + + + + + +packages/core/test/util.test.mjs->packages/core/util.mjs + + + + + +packages/core/test/value.test.mjs + + +value.test.mjs + + + + + +packages/core/test/value.test.mjs->packages/core/value.mjs + + + + + +packages/embed/embed.js + + +embed.js + + + + + +packages/eval/evaluate.mjs + + +evaluate.mjs + + + + + +packages/eval/evaluate.mjs->packages/core/index.mjs + + + + + +packages/eval/shapeshifter.mjs + + +shapeshifter.mjs + + + + + +packages/eval/evaluate.mjs->packages/eval/shapeshifter.mjs + + + + + +packages/eval/shapeshifter.mjs->packages/core/index.mjs + + + + + +packages/eval/shift-traverser/index.js + + +index.js + + + + + +packages/eval/shapeshifter.mjs->packages/eval/shift-traverser/index.js + + + + + +packages/eval/index.mjs + + +index.mjs + + + + + +packages/eval/index.mjs->packages/eval/evaluate.mjs + + + + + +packages/eval/test/evaluate.test.mjs + + +evaluate.test.mjs + + + + + +packages/eval/test/evaluate.test.mjs->packages/core/index.mjs + + + + + +packages/eval/test/evaluate.test.mjs->packages/eval/evaluate.mjs + + + + + +packages/mini/index.mjs + + +index.mjs + + + + + +packages/eval/test/evaluate.test.mjs->packages/mini/index.mjs + + + + + +packages/mini/krill-parser.js + + +krill-parser.js + + + + + +packages/mini/index.mjs->packages/mini/krill-parser.js + + + + + +packages/mini/mini.mjs + + +mini.mjs + + + + + +packages/mini/index.mjs->packages/mini/mini.mjs + + + + + +packages/eval/test/shapeshifter.test.mjs + + +shapeshifter.test.mjs + + + + + +packages/eval/test/shapeshifter.test.mjs->packages/eval/shapeshifter.mjs + + + + + +packages/midi/index.mjs + + +index.mjs + + + + + +packages/midi/midi.mjs + + +midi.mjs + + + + + +packages/midi/index.mjs->packages/midi/midi.mjs + + + + + +packages/midi/midi.mjs->packages/core/index.mjs + + + + + +packages/tone/index.mjs + + +index.mjs + + + + + +packages/midi/midi.mjs->packages/tone/index.mjs + + + + + +packages/tone/draw.mjs + + +draw.mjs + + + + + +packages/tone/index.mjs->packages/tone/draw.mjs + + + + + +packages/tone/tone.mjs + + +tone.mjs + + + + + +packages/tone/index.mjs->packages/tone/tone.mjs + + + + + +packages/tone/pianoroll.mjs + + +pianoroll.mjs + + + + + +packages/tone/index.mjs->packages/tone/pianoroll.mjs + + + + + +packages/tone/ui.mjs + + +ui.mjs + + + + + +packages/tone/index.mjs->packages/tone/ui.mjs + + + + + +packages/mini/mini.mjs->packages/core/index.mjs + + + + + +packages/mini/mini.mjs->packages/eval/shapeshifter.mjs + + + + + +packages/mini/mini.mjs->packages/mini/krill-parser.js + + + + + +packages/mini/test/mini.test.mjs + + +mini.test.mjs + + + + + +packages/mini/test/mini.test.mjs->packages/core/euclid.mjs + + + + + +packages/mini/test/mini.test.mjs->packages/mini/mini.mjs + + + + + +packages/osc/osc.mjs + + +osc.mjs + + + + + +packages/osc/osc.mjs->packages/core/index.mjs + + + + + +packages/osc/server.js + + +server.js + + + + + +packages/osc/tidal-sniffer.js + + +tidal-sniffer.js + + + + + +packages/react/dist/index.cjs.js + + +index.cjs.js + + + + + +packages/react/dist/index.cjs.js->packages/core/util.mjs + + + + + +packages/react/dist/index.cjs.js->packages/core/index.mjs + + + + + +packages/react/dist/index.cjs.js->packages/eval/index.mjs + + + + + +packages/react/dist/index.cjs.js->packages/midi/index.mjs + + + + + +packages/react/dist/index.cjs.js->packages/tone/index.mjs + + + + + +packages/react/dist/index.es.js + + +index.es.js + + + + + +packages/react/dist/index.es.js->packages/core/util.mjs + + + + + +packages/react/dist/index.es.js->packages/core/index.mjs + + + + + +packages/react/dist/index.es.js->packages/eval/index.mjs + + + + + +packages/react/dist/index.es.js->packages/midi/index.mjs + + + + + +packages/react/dist/index.es.js->packages/tone/index.mjs + + + + + +packages/react/dist/index.es.js->packages/react/dist/index.cjs.js + + + + + +packages/react/package.json + + +package.json + + + + + +packages/react/postcss.config.js + + +postcss.config.js + + + + + +packages/react/src/App.jsx + + +App.jsx + + + + + +packages/react/src/App.jsx->packages/core/index.mjs + + + + + +packages/react/src/App.jsx->packages/eval/index.mjs + + + + + +packages/react/src/App.jsx->packages/mini/index.mjs + + + + + +packages/react/src/App.jsx->packages/midi/index.mjs + + + + + +packages/react/src/App.jsx->packages/tone/index.mjs + + + + + +packages/react/src/App.jsx->packages/react/dist/index.cjs.js + + + + + +packages/react/src/components/MiniRepl.jsx + + +MiniRepl.jsx + + + + + +packages/react/src/App.jsx->packages/react/src/components/MiniRepl.jsx + + + + + +packages/tonal/index.mjs + + +index.mjs + + + + + +packages/react/src/App.jsx->packages/tonal/index.mjs + + + + + +packages/webaudio/index.mjs + + +index.mjs + + + + + +packages/react/src/App.jsx->packages/webaudio/index.mjs + + + + + +packages/xen/index.mjs + + +index.mjs + + + + + +packages/react/src/App.jsx->packages/xen/index.mjs + + + + + +packages/react/src/components/MiniRepl.jsx->packages/react/dist/index.cjs.js + + + + + +packages/react/src/components/CodeMirror6.jsx + + +CodeMirror6.jsx + + + + + +packages/react/src/components/MiniRepl.jsx->packages/react/src/components/CodeMirror6.jsx + + + + + +packages/react/src/cx.js + + +cx.js + + + + + +packages/react/src/components/MiniRepl.jsx->packages/react/src/cx.js + + + + + +packages/react/src/hooks/useHighlighting.mjs + + +useHighlighting.mjs + + + + + +packages/react/src/components/MiniRepl.jsx->packages/react/src/hooks/useHighlighting.mjs + + + + + +packages/react/src/hooks/useRepl.mjs + + +useRepl.mjs + + + + + +packages/react/src/components/MiniRepl.jsx->packages/react/src/hooks/useRepl.mjs + + + + + +packages/react/src/components/MiniRepl.module.css + + +MiniRepl.module.css + + + + + +packages/react/src/components/MiniRepl.jsx->packages/react/src/components/MiniRepl.module.css + + + + + +packages/react/src/components/style.css + + +style.css + + + + + +packages/react/src/components/MiniRepl.jsx->packages/react/src/components/style.css + + + + + +packages/tonal/tonal.mjs + + +tonal.mjs + + + + + +packages/tonal/index.mjs->packages/tonal/tonal.mjs + + + + + +packages/tonal/voicings.mjs + + +voicings.mjs + + + + + +packages/tonal/index.mjs->packages/tonal/voicings.mjs + + + + + +packages/webaudio/clockworker.mjs + + +clockworker.mjs + + + + + +packages/webaudio/index.mjs->packages/webaudio/clockworker.mjs + + + + + +packages/webaudio/sampler.mjs + + +sampler.mjs + + + + + +packages/webaudio/index.mjs->packages/webaudio/sampler.mjs + + + + + +packages/webaudio/scheduler.mjs + + +scheduler.mjs + + + + + +packages/webaudio/index.mjs->packages/webaudio/scheduler.mjs + + + + + +packages/webaudio/webaudio.mjs + + +webaudio.mjs + + + + + +packages/webaudio/index.mjs->packages/webaudio/webaudio.mjs + + + + + +packages/xen/tune.mjs + + +tune.mjs + + + + + +packages/xen/index.mjs->packages/xen/tune.mjs + + + + + +packages/xen/xen.mjs + + +xen.mjs + + + + + +packages/xen/index.mjs->packages/xen/xen.mjs + + + + + +packages/react/src/components/CodeMirror6.jsx->packages/react/dist/index.cjs.js + + + + + +packages/react/src/themes/material-palenight.js + + +material-palenight.js + + + + + +packages/react/src/components/CodeMirror6.jsx->packages/react/src/themes/material-palenight.js + + + + + +packages/react/src/hooks/useHighlighting.mjs->packages/tone/index.mjs + + + + + +packages/react/src/hooks/useHighlighting.mjs->packages/react/dist/index.cjs.js + + + + + +packages/react/src/hooks/useHighlighting.mjs->packages/react/src/components/CodeMirror6.jsx + + + + + +packages/react/src/hooks/useRepl.mjs->packages/core/util.mjs + + + + + +packages/react/src/hooks/useRepl.mjs->packages/eval/index.mjs + + + + + +packages/react/src/hooks/useRepl.mjs->packages/react/dist/index.cjs.js + + + + + +packages/react/src/hooks/useCycle.mjs + + +useCycle.mjs + + + + + +packages/react/src/hooks/useRepl.mjs->packages/react/src/hooks/useCycle.mjs + + + + + +packages/react/src/hooks/usePostMessage.mjs + + +usePostMessage.mjs + + + + + +packages/react/src/hooks/useRepl.mjs->packages/react/src/hooks/usePostMessage.mjs + + + + + +packages/react/src/hooks/useCycle.mjs->packages/core/index.mjs + + + + + +packages/react/src/hooks/useCycle.mjs->packages/tone/index.mjs + + + + + +packages/react/src/hooks/useCycle.mjs->packages/react/dist/index.cjs.js + + + + + +packages/react/src/hooks/usePostMessage.mjs->packages/react/dist/index.cjs.js + + + + + +packages/react/src/hooks/useWebMidi.mjs + + +useWebMidi.mjs + + + + + +packages/react/src/hooks/useWebMidi.mjs->packages/midi/index.mjs + + + + + +packages/react/src/hooks/useWebMidi.mjs->packages/react/dist/index.cjs.js + + + + + +packages/react/src/index.js + + +index.js + + + + + +packages/react/src/index.js->packages/react/src/components/MiniRepl.jsx + + + + + +packages/react/src/index.js->packages/react/src/components/CodeMirror6.jsx + + + + + +packages/react/src/index.js->packages/react/src/cx.js + + + + + +packages/react/src/index.js->packages/react/src/hooks/useHighlighting.mjs + + + + + +packages/react/src/index.js->packages/react/src/hooks/useRepl.mjs + + + + + +packages/react/src/index.js->packages/react/src/hooks/useCycle.mjs + + + + + +packages/react/src/index.js->packages/react/src/hooks/usePostMessage.mjs + + + + + +packages/react/src/index.js->packages/react/src/hooks/useWebMidi.mjs + + + + + +packages/react/src/main.jsx + + +main.jsx + + + + + +packages/react/src/main.jsx->packages/react/dist/index.cjs.js + + + + + +packages/react/src/main.jsx->packages/react/src/App.jsx + + + + + +packages/react/tailwind.config.js + + +tailwind.config.js + + + + + +packages/react/vite.config.js + + +vite.config.js + + + + + +packages/react/vite.config.js->packages/react/package.json + + + + + +packages/serial/serial.mjs + + +serial.mjs + + + + + +packages/serial/serial.mjs->packages/core/index.mjs + + + + + +packages/soundfonts/convert.js + + +convert.js + + + + + +packages/soundfonts/fontloader.mjs + + +fontloader.mjs + + + + + +packages/soundfonts/index.mjs + + +index.mjs + + + + + +packages/soundfonts/index.mjs->packages/soundfonts/fontloader.mjs + + + + + +packages/soundfonts/list.mjs + + +list.mjs + + + + + +packages/soundfonts/index.mjs->packages/soundfonts/list.mjs + + + + + +packages/tonal/tonal.mjs->packages/core/index.mjs + + + + + +packages/tonal/voicings.mjs->packages/core/index.mjs + + + + + +packages/tonal/test/tonal.test.mjs + + +tonal.test.mjs + + + + + +packages/tonal/test/tonal.test.mjs->packages/core/index.mjs + + + + + +packages/tonal/test/tonal.test.mjs->packages/tonal/tonal.mjs + + + + + +packages/tone/draw.mjs->packages/core/index.mjs + + + + + +packages/tone/draw.mjs->packages/tone/tone.mjs + + + + + +packages/tone/tone.mjs->packages/core/util.mjs + + + + + +packages/tone/tone.mjs->packages/core/index.mjs + + + + + +packages/tone/pianoroll.mjs->packages/core/index.mjs + + + + + +packages/tone/ui.mjs->packages/tone/tone.mjs + + + + + +packages/tone/test/tone.test.mjs + + +tone.test.mjs + + + + + +packages/tone/test/tone.test.mjs->packages/core/index.mjs + + + + + +packages/tone/test/tone.test.mjs->packages/tone/tone.mjs + + + + + +packages/webaudio/scheduler.mjs->packages/core/index.mjs + + + + + +packages/webaudio/scheduler.mjs->packages/webaudio/clockworker.mjs + + + + + +packages/webaudio/webaudio.mjs->packages/core/index.mjs + + + + + +packages/webaudio/webaudio.mjs->packages/webaudio/sampler.mjs + + + + + +packages/webdirt/index.mjs + + +index.mjs + + + + + +packages/webdirt/webdirt.mjs + + +webdirt.mjs + + + + + +packages/webdirt/index.mjs->packages/webdirt/webdirt.mjs + + + + + +packages/webdirt/webdirt.mjs->packages/core/index.mjs + + + + + +packages/webdirt/webdirt.mjs->packages/webaudio/index.mjs + + + + + +packages/xen/tune.mjs->packages/core/index.mjs + + + + + +packages/xen/tunejs.js + + +tunejs.js + + + + + +packages/xen/tune.mjs->packages/xen/tunejs.js + + + + + +packages/xen/xen.mjs->packages/core/index.mjs + + + + + +packages/xen/test/xen.test.mjs + + +xen.test.mjs + + + + + +packages/xen/test/xen.test.mjs->packages/xen/xen.mjs + + + + + diff --git a/package-lock.json b/package-lock.json index 73d2c599..3b08747e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "jsdoc-json": "^2.0.2", "jsdoc-to-markdown": "^7.1.1", "lerna": "^4.0.0", + "rollup-plugin-visualizer": "^5.8.1", "vitest": "^0.21.1" } }, @@ -4256,6 +4257,15 @@ "clone": "^1.0.2" } }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -6506,6 +6516,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -6722,6 +6747,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -7998,6 +8035,18 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -8575,6 +8624,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -9178,18 +9244,6 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, - "node_modules/postcss/node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -9941,6 +9995,91 @@ "fsevents": "~2.3.2" } }, + "node_modules/rollup-plugin-visualizer": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.8.1.tgz", + "integrity": "sha512-NBT/xN/LWCwDM2/j5vYmjzpEAKHyclo/8Cv8AfTCwgADAG+tLJDy1vzxMw6NO0dSDjmTeRELD9UU3FwknLv0GQ==", + "dev": true, + "dependencies": { + "nanoid": "^3.3.4", + "open": "^8.4.0", + "source-map": "^0.7.3", + "yargs": "^17.5.1" + }, + "bin": { + "rollup-plugin-visualizer": "dist/bin/cli.js" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "rollup": "^2.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -16012,6 +16151,12 @@ "clone": "^1.0.2" } }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -17637,6 +17782,12 @@ "has-tostringtag": "^1.0.0" } }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -17787,6 +17938,15 @@ "call-bind": "^1.0.2" } }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -18795,6 +18955,12 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true + }, "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -19238,6 +19404,17 @@ "mimic-fn": "^2.1.0" } }, + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, "optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -19613,14 +19790,6 @@ "nanoid": "^3.3.4", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" - }, - "dependencies": { - "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "dev": true - } } }, "postcss-js": { @@ -20325,6 +20494,64 @@ "fsevents": "~2.3.2" } }, + "rollup-plugin-visualizer": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.8.1.tgz", + "integrity": "sha512-NBT/xN/LWCwDM2/j5vYmjzpEAKHyclo/8Cv8AfTCwgADAG+tLJDy1vzxMw6NO0dSDjmTeRELD9UU3FwknLv0GQ==", + "dev": true, + "requires": { + "nanoid": "^3.3.4", + "open": "^8.4.0", + "source-map": "^0.7.3", + "yargs": "^17.5.1" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + } + } + }, "run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", diff --git a/package.json b/package.json index bf045288..97f26247 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "osc": "cd packages/osc && npm run server", "build": "rm -rf out && cd repl && npm run build && cd ../tutorial && npm run build", "preview": "npx serve ./out", - "deploy": "gh-pages -d out", + "deploy": "NODE_DEBUG=gh-pages gh-pages -d out", "jsdoc": "jsdoc packages/ -c jsdoc.config.json", "jsdoc-json": "jsdoc packages/ --template ./node_modules/jsdoc-json --destination doc.json -c jsdoc.config.json" }, @@ -46,6 +46,7 @@ "jsdoc-json": "^2.0.2", "jsdoc-to-markdown": "^7.1.1", "lerna": "^4.0.0", + "rollup-plugin-visualizer": "^5.8.1", "vitest": "^0.21.1" } } diff --git a/packages/README.md b/packages/README.md new file mode 100644 index 00000000..5eaa07c6 --- /dev/null +++ b/packages/README.md @@ -0,0 +1,15 @@ +# Packages + +Each folder represents one of the @strudel.cycles/* packages [published to npm](https://www.npmjs.com/org/strudel.cycles). + +To understand how those pieces connect, refer to the [Technical Manual](https://github.com/tidalcycles/strudel/wiki/Technical-Manual) or the individual READMEs. + +This is a graphical view of all the packages: [full screen](https://raw.githubusercontent.com/tidalcycles/strudel/main/dependencies.svg) + +![dependencies](https://raw.githubusercontent.com/tidalcycles/strudel/main/dependencies.svg) + +Generated with + +```sh +npx depcruise --include-only "^packages" -X "node_modules" --output-type dot packages | dot -T svg > dependencygraph.svg +``` diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index b9d8a257..c5c19644 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -11,7 +11,7 @@ const generic_params = [ /** * Select a sound / sample by name. * - *
+ *
* show all sounds * * 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) @@ -23,7 +23,7 @@ const generic_params = [ * @name s * @param {string | Pattern} sound The sound / pattern of sounds to pick * @example - * s("bd hh").osc() + * s("bd hh").out() * */ ['s', 's', 'sound'], @@ -129,7 +129,7 @@ const generic_params = [ * @name bandf * @param {number | Pattern} frequency center frequency * @example - * s("bd sd").bandf("<1000 2000 4000 8000>").osc() + * s("bd sd,hh*3").bandf("<1000 2000 4000 8000>").out() * */ ['f', 'bandf', 'A pattern of numbers from 0 to 1. Sets the center frequency of the band-pass filter.'], @@ -140,7 +140,7 @@ const generic_params = [ * @name bandq * @param {number | Pattern} q q factor * @example - * s("bd sd").bandf(2000).bandq("<.2 .9>").osc() + * s("bd sd").bandf(500).bandq("<0 1 2 3>").out() * */ ['f', 'bandq', 'a pattern of anumbers from 0 to 1. Sets the q-factor of the band-pass filter.'], @@ -202,7 +202,7 @@ const generic_params = [ * @name crush * @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction). * @example - * s(",hh*3,jvbass*2").fast(2).crush("<16 8 7 6 5 4 3 2>").osc() + * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>").out() * */ [ @@ -216,7 +216,7 @@ const generic_params = [ * @name coarse * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. * @example - * s("xmas").coarse("<1 4 8 16 32>").osc() + * s("bd sd,hh*4").coarse("<1 4 8 16 32>").out() * */ [ @@ -253,7 +253,7 @@ const generic_params = [ * @name cutoff * @param {number | Pattern} frequency audible between 0 and 20000 * @example - * s("bd,hh*2,<~ sd>").fast(2).cutoff("<4000 2000 1000 500 200 100>").osc() + * s("bd sd,hh*3").cutoff("<4000 2000 1000 500 200 100>").out() * */ // TODO: add lpf synonym @@ -264,7 +264,7 @@ const generic_params = [ * @name hcutoff * @param {number | Pattern} frequency audible between 0 and 20000 * @example - * s("bd,hh*2,<~ sd>").fast(2).hcutoff("<4000 2000 1000 500 200 100>").osc() + * s("bd sd,hh*4").hcutoff("<4000 2000 1000 500 200 100>").out() * */ // TODO: add hpf synonym @@ -274,12 +274,12 @@ const generic_params = [ 'a pattern of numbers from 0 to 1. Applies the cutoff frequency of the high-pass filter. Also has alias @hpf@', ], /** - * Applies the cutoff frequency of the high-pass filter. + * Applies the resonance of the high-pass filter. * * @name hresonance * @param {number | Pattern} q resonance factor between 0 and 1 * @example - * s("bd,hh*2,<~ sd>").fast(2).hcutoff(2000).hresonance("<0 .2 .4 .6>").osc() + * s("bd sd,hh*4").hcutoff(2000).hresonance("<0 10 20 30>").out() * */ [ @@ -294,13 +294,13 @@ const generic_params = [ * @name resonance * @param {number | Pattern} q resonance factor between 0 and 1 * @example - * s("bd,hh*2,<~ sd>").fast(2).cutoff(2000).resonance("<0 .2 .4 .6>").osc() + * s("bd sd,hh*4").cutoff(2000).resonance("<0 10 20 30>").out() * */ ['f', 'resonance', 'a pattern of numbers from 0 to 1. Specifies the resonance of the low-pass filter.'], // TODO: add lpq synonym? /** - * Set detune of oscillators. Works only with some synths, see tidal doc + * DJ filter, below 0.5 is low pass filter, above is high pass filter. * * @name djf * @param {number | Pattern} cutoff below 0.5 is low pass filter, above is high pass filter @@ -493,7 +493,7 @@ const generic_params = [ * @name pan * @param {number | Pattern} pan between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel) * @example - * s("[bd hh]*2").pan("<.5 1 .5 0>").osc() + * s("[bd hh]*2").pan("<.5 1 .5 0>").out() * */ [ @@ -591,7 +591,7 @@ const generic_params = [ * @name shape * @param {number | Pattern} distortion between 0 and 1 * @example - * s("bd sd").shape("<0 .2 .4 .6 .8 1>").osc() + * s("bd sd,hh*4").shape("<0 .2 .4 .6 .8>").out() * */ [ @@ -648,16 +648,16 @@ const generic_params = [ // ['f', 'tomdecay', ''], // ['f', 'vcfegint', ''], // ['f', 'vcoegint', ''], + // TODO: Use a rest (~) to override the effect <- vowel /** * * Formant filter to make things sound like vowels. * * @name vowel - * @param {string | Pattern} vowel You can use a e i o u. Use a rest (~) to override the effect + * @param {string | Pattern} vowel You can use a e i o u. * @example - * vowel("a e i [o u]").slow(2) - * .n("<[0,7]!4 [2,7]!4>") - * .s('supersquare').osc() + * note("c2 >").s('sawtooth') + * .vowel(">").out() * */ [ diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index db1df876..63b11387 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -513,11 +513,14 @@ export class Pattern { } /** - * Assumes a numerical pattern, containing unipolar values in the range 0 .. - * 1. Returns a new pattern with values scaled to the given min/max range. - * @param {Number} min - * @param {Number} max + * Assumes a numerical pattern, containing unipolar values in the range 0 .. 1. + * Returns a new pattern with values scaled to the given min/max range. + * Most useful in combination with continuous patterns. + * @name range + * @memberof Pattern * @returns Pattern + * @example + * s("bd sd,hh*4").cutoff(sine.range(500,2000).slow(4)).out() */ range(min, max) { return this.mul(max - min).add(min); @@ -683,6 +686,16 @@ export class Pattern { return func(this); } + /** + * Layers the result of the given function(s). Like {@link superimpose}, but without the original pattern: + * @name layer + * @memberof Pattern + * @returns Pattern + * @example + * "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*4" + * .layer(x=>x.add("0,2")) + * .scale('C minor').note().out() + */ layer(...funcs) { return stack(...funcs.map((func) => func(this))); } @@ -734,14 +747,14 @@ export class Pattern { } /** - * Speed up a pattern by the given factor. + * Speed up a pattern by the given factor. Used by "*" in mini notation. * * @name fast * @memberof Pattern * @param {number | Pattern} factor speed up factor * @returns Pattern * @example - * seq(e5, b4, d5, c5).fast(2) + * s(" hh").fast(2).out() // s("[ hh]*2").out() */ _fast(factor) { const fastQuery = this.withQueryTime((t) => t.mul(factor)); @@ -749,14 +762,14 @@ export class Pattern { } /** - * Slow down a pattern over the given number of cycles. + * Slow down a pattern over the given number of cycles. Like the "/" operator in mini notation. * * @name slow * @memberof Pattern * @param {number | Pattern} factor slow down factor * @returns Pattern * @example - * seq(e5, b4, d5, c5).slow(2) + * s(" hh").slow(2).out() // s("[ hh]/2").out() */ _slow(factor) { return this._fast(Fraction(1).div(factor)); @@ -795,14 +808,32 @@ export class Pattern { return this._fast(cpm / 60); } + /** + * Nudge a pattern to start earlier in time. Equivalent of Tidal's <~ operator + * + * @name early + * @memberof Pattern + * @param {number | Pattern} cycles number of cycles to nudge left + * @returns Pattern + * @example + * "bd ~".stack("hh ~".early(.1)).s().out() + */ _early(offset) { - // Equivalent of Tidal's <~ operator offset = Fraction(offset); return this.withQueryTime((t) => t.add(offset)).withHapTime((t) => t.sub(offset)); } + /** + * Nudge a pattern to start later in time. Equivalent of Tidal's ~> operator + * + * @name late + * @memberof Pattern + * @param {number | Pattern} cycles number of cycles to nudge right + * @returns Pattern + * @example + * "bd ~".stack("hh ~".late(.1)).s().out() + */ _late(offset) { - // Equivalent of Tidal's ~> operator offset = Fraction(offset); return this._early(Fraction(0).sub(offset)); } @@ -829,6 +860,17 @@ export class Pattern { return this._zoom(0, t)._slow(t); } + /** + * Applies the given structure to the pattern: + * + * @name struct + * @memberof Pattern + * @returns Pattern + * @example + * "c3,eb3,g3" + * .struct("x ~ x ~ ~ x ~ x ~ ~ ~ x ~ x ~ ~") + * .slow(4).note().out() + */ // struct(...binary_pats) { // // Re structure the pattern according to a binary pattern (false values are dropped) // const binary_pat = sequence(binary_pats); @@ -876,6 +918,16 @@ export class Pattern { return this.invert(); } + /** + * Applies the given function whenever the given pattern is in a true state. + * @name when + * @memberof Pattern + * @param {Pattern} binary_pat + * @param {function} func + * @returns Pattern + * @example + * "c3 eb3 g3".when("<0 1>/2", x=>x.sub(5)) + */ when(binary_pat, func) { //binary_pat = sequence(binary_pat) const true_pat = binary_pat._filterValues(id); @@ -885,10 +937,47 @@ export class Pattern { return stack(with_pat, without_pat); } + /** + * Superimposes the function result on top of the original pattern, delayed by the given time. + * @name off + * @memberof Pattern + * @param {Pattern | number} time offset time + * @param {function} func function to apply + * @returns Pattern + * @example + * "c3 eb3 g3".off(1/8, x=>x.add(7)) + */ off(time_pat, func) { return stack(this, func(this.late(time_pat))); } + /** + * Applies the given function every n cycles. + * @name every + * @memberof Pattern + * @param {number} n how many cycles + * @param {function} func function to apply + * @returns Pattern + * @example + * note("c3 d3 e3 g3").every(4, x=>x.rev()).out() + */ + every(n, func) { + const pat = this; + const pats = Array(n - 1).fill(pat); + // pats.unshift(func(pat)); + pats.push(func(pat)); + return slowcatPrime(...pats); + } + /** + * Applies the given function every n cycles, starting from the first cycle. + * @name every + * @memberof Pattern + * @param {number} n how many cycles + * @param {function} func function to apply + * @returns Pattern + * @example + * note("c3 d3 e3 g3").every(4, x=>x.rev()).out() + */ every(n, func) { const pat = this; const pats = Array(n - 1).fill(pat); @@ -896,6 +985,23 @@ export class Pattern { return slowcatPrime(...pats); } + /** + * Applies the given function every n cycles, starting from the last cycle. + * @name each + * @memberof Pattern + * @param {number} n how many cycles + * @param {function} func function to apply + * @returns Pattern + * @example + * note("c3 d3 e3 g3").every(4, x=>x.rev()).out() + */ + each(n, func) { + const pat = this; + const pats = Array(n - 1).fill(pat); + pats.push(func(pat)); + return slowcatPrime(...pats); + } + /** * Returns a new pattern where every other cycle is played once, twice as * fast, and offset in time by one quarter of a cycle. Creates a kind of @@ -906,6 +1012,15 @@ export class Pattern { return this.when(slowcat(false, true), (x) => fastcat(x, silence)._late(0.25)); } + /** + * Reverse all haps in a pattern + * + * @name rev + * @memberof Pattern + * @returns Pattern + * @example + * "c3 d3 e3 g3".rev() + */ rev() { const pat = this; const query = function (state) { @@ -948,6 +1063,15 @@ export class Pattern { return this.juxBy(1, func); } + /** + * Stacks the given pattern(s) to the current pattern. + * @name stack + * @memberof Pattern + * @example + * s("hh*2").stack( + * n("c2(3,8)") + * ).out() + */ stack(...pats) { return stack(this, ...pats); } @@ -956,11 +1080,28 @@ export class Pattern { return sequence(this, ...pats); } - // shorthand for sequence + /** + * Appends the given pattern(s) to the current pattern. Synonyms: .sequence .fastcat + * @name seq + * @memberof Pattern + * @example + * s("hh*2").seq( + * n("c2(3,8)") + * ).out() + */ seq(...pats) { return sequence(this, ...pats); } + /** + * Appends the given pattern(s) to the next cycle. Synonym: .slowcat + * @name cat + * @memberof Pattern + * @example + * s("hh*2").cat( + * n("c2(3,8)") + * ).out() + */ cat(...pats) { return cat(this, ...pats); } @@ -973,6 +1114,16 @@ export class Pattern { return slowcat(this, ...pats); } + /** + * Superimposes the result of the given function(s) on top of the original pattern: + * @name superimpose + * @memberof Pattern + * @returns Pattern + * @example + * "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*4" + * .superimpose(x=>x.add(2)) + * .scale('C minor').note().out() + */ superimpose(...funcs) { return this.stack(...funcs.map((func) => func(this))); } @@ -985,24 +1136,67 @@ export class Pattern { return this.stutWith(times, time, (pat, i) => pat.velocity(Math.pow(feedback, i))); } - // these might change with: https://github.com/tidalcycles/Tidal/issues/902 + /** + * Superimpose and offset multiple times, applying the given function each time. + * @name echoWith + * @memberof Pattern + * @returns Pattern + * @param {number} times how many times to repeat + * @param {number} time cycle offset between iterations + * @param {function} func function to apply, given the pattern and the iteration index + * @example + * "<0 [2 4]>" + * .echoWith(4, 1/8, (p,n) => p.add(n*2)) + * .scale('C minor').note().legato(.2).out() + */ _echoWith(times, time, func) { return stack(...listRange(0, times - 1).map((i) => func(this.late(Fraction(time).mul(i)), i))); } + /** + * Superimpose and offset multiple times, gradually decreasing the velocity + * @name echo + * @memberof Pattern + * @returns Pattern + * @example + * s("bd sd").echo(3, 1/6, .8).out() + */ _echo(times, time, feedback) { return this._echoWith(times, time, (pat, i) => pat.velocity(Math.pow(feedback, i))); } + /** + * Divides a pattern into a given number of subdivisions, plays the subdivisions in order, but increments the starting subdivision each cycle. The pattern wraps to the first subdivision after the last subdivision is played. + * @name iter + * @memberof Pattern + * @returns Pattern + * @example + * note("0 1 2 3".scale('A minor')).iter(4).out() + */ iter(times, back = false) { return slowcat(...listRange(0, times - 1).map((i) => (back ? this.late(i / times) : this.early(i / times)))); } - // known as iter' in tidalcycles + /** + * Like `iter`, but plays the subdivisions in reverse order. Known as iter' in tidalcycles + * @name iterBack + * @memberof Pattern + * @returns Pattern + * @example + * note("0 1 2 3".scale('A minor')).iterBack(4).out() + */ iterBack(times) { return this.iter(times, true); } + /** + * Divides a pattern into a given number of parts, then cycles through those parts in turn, applying the given function to each part in turn (one part per cycle). + * @name chunk + * @memberof Pattern + * @returns Pattern + * @example + * "0 1 2 3".chunk(4, x=>x.add(7)).scale('A minor').note().out() + */ _chunk(n, func, back = false) { const binary = Array(n - 1).fill(false); binary.unshift(true); @@ -1010,6 +1204,14 @@ export class Pattern { return this.when(binary_pat, func); } + /** + * Like `chunk`, but cycles through the parts in reverse order. Known as chunk' in tidalcycles + * @name chunkBack + * @memberof Pattern + * @returns Pattern + * @example + * "0 1 2 3".chunkBack(4, x=>x.add(7)).scale('A minor').note().out() + */ _chunkBack(n, func) { return this._chunk(n, func, true); } @@ -1184,6 +1386,11 @@ Pattern.prototype.patternified = [ 'slow', 'velocity', ]; + +// aliases +export const polyrhythm = stack; +export const pr = stack; + // methods that create patterns, which are added to patternified Pattern methods Pattern.prototype.factories = { pure, @@ -1206,12 +1413,11 @@ Pattern.prototype.factories = { // Nothing export const silence = new Pattern((_) => []); -/** A discrete value that repeats once per cycle: +/** A discrete value that repeats once per cycle. * - * @param {any} value - The value to repeat * @returns {Pattern} * @example - * pure('e4') + * pure('e4') // "e4" */ export function pure(value) { function query(state) { @@ -1241,12 +1447,11 @@ export function reify(thing) { return pure(thing); } -/** The given items are played at the same time at the same length: +/** The given items are played at the same time at the same length. * - * @param {...any} items - The items to stack * @return {Pattern} * @example - * stack(g3, b3, [e4, d4]) + * stack(g3, b3, [e4, d4]) // "g3,b3,[e4,d4]" */ export function stack(...pats) { // Array test here is to avoid infinite recursions.. @@ -1259,7 +1464,6 @@ export function stack(...pats) { * * synonyms: {@link cat} * - * @param {...any} items - The items to concatenate * @return {Pattern} * @example * slowcat(e5, b4, [d5, c5]) @@ -1315,16 +1519,22 @@ export function fastcat(...pats) { return slowcat(...pats)._fast(pats.length); } -/** See {@link slowcat} */ +/** The given items are con**cat**enated, where each one takes one cycle. Synonym: slowcat + * + * @param {...any} items - The items to concatenate + * @return {Pattern} + * @example + * cat(e5, b4, [d5, c5]) // "" + * + */ export function cat(...pats) { return slowcat(...pats); } -/** Like {@link fastcat}, but where each step has a temporal weight: - * @param {...Array} items - The items to concatenate +/** Like {@link seq}, but each step has a length, relative to the whole. * @return {Pattern} * @example - * timeCat([3,e3],[1, g3]) + * timeCat([3,e3],[1, g3]) // "e3@3 g3" */ export function timeCat(...timepats) { const total = timepats.map((a) => a[0]).reduce((a, b) => a.add(b), Fraction(0)); @@ -1343,7 +1553,11 @@ export function sequence(...pats) { return fastcat(...pats); } -/** See {@link fastcat} */ +/** Like **cat**, but the items are crammed into one cycle. Synonyms: fastcat, sequence + * @example + * seq(e5, b4, [d5, c5]) // "e5 b4 [d5 c5]" + * + */ export function seq(...pats) { return fastcat(...pats); } @@ -1392,20 +1606,6 @@ export function pm(...args) { polymeter(...args); } -export function polyrhythm(...xs) { - const seqs = xs.map((a) => sequence(a)); - - if (seqs.length == 0) { - return silence; - } - return stack(...seqs); -} - -// alias -export function pr(args) { - polyrhythm(args); -} - export const add = curry((a, pat) => pat.add(a)); export const chop = curry((a, pat) => pat.chop(a)); export const chunk = curry((a, pat) => pat.chunk(a)); diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index a13674f1..8637c30e 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th */ import { Hap } from './hap.mjs'; -import { Pattern, fastcat, reify, silence, stack } from './pattern.mjs'; +import { Pattern, fastcat, reify, silence, stack, isPattern } from './pattern.mjs'; import Fraction from './fraction.mjs'; import { id } from './util.mjs'; @@ -74,7 +74,7 @@ export const square2 = square._toBipolar(); * * @return {Pattern} * @example - * triangle.segment(2).range(0,7).scale('C minor') + * tri.segment(8).range(0,7).scale('C minor') * */ export const tri = fastcat(isaw, saw); @@ -111,7 +111,17 @@ const timeToRandsPrime = (seed, n) => { const timeToRands = (t, n) => timeToRandsPrime(timeToIntSeed(t), n); /** - * A continuous pattern of random numbers, between 0 and 1 + * + */ + +/** + * A continuous pattern of random numbers, between 0 and 1. + * + * @name rand + * @example + * // randomly change the cutoff + * s("bd sd,hh*4").cutoff(rand.range(500,2000)).out() + * */ export const rand = signal(timeToRand); /** @@ -124,6 +134,17 @@ export const brandBy = (pPat) => reify(pPat).fmap(_brandBy).innerJoin(); export const brand = _brandBy(0.5); export const _irand = (i) => rand.fmap((x) => Math.trunc(x * i)); + +/** + * A continuous pattern of random integers, between 0 and n-1. + * + * @name irand + * @param {number} n max value (exclusive) + * @example + * // randomly select scale notes from 0 - 7 (= C to C) + * irand(8).struct("x(3,8)").scale('C minor').note().out() + * + */ export const irand = (ipat) => reify(ipat).fmap(_irand).innerJoin(); export const __chooseWith = (pat, xs) => { @@ -156,8 +177,8 @@ export const chooseInWith = (pat, xs) => { }; /** - * Chooses randomly from the given list of values. - * @param {...any} xs + * Chooses randomly from the given list of elements. + * @param {...any} xs values / patterns to choose from. * @returns {Pattern} - a continuous pattern. */ export const choose = (...xs) => chooseWith(rand, xs); @@ -183,6 +204,14 @@ Pattern.prototype.choose2 = function (...xs) { return chooseWith(this._fromBipolar(), xs); }; +/** + * Picks one of the elements at random each cycle. + * @returns {Pattern} + * @example + * chooseCycles("bd", "hh", "sd").s().fast(4).out() + * @example + * "bd | hh | sd".s().fast(4).out() + */ export const chooseCycles = (...xs) => chooseInWith(rand.segment(1), xs); export const randcat = chooseCycles; @@ -217,20 +246,68 @@ export const perlinWith = (pat) => { return pat.sub(pata).fmap(interp).appBoth(pata.fmap(timeToRand)).appBoth(patb.fmap(timeToRand)); }; +/** + * Generates a continuous pattern of [perlin noise](https://en.wikipedia.org/wiki/Perlin_noise), in the range 0..1. + * + * @name perlin + * @example + * // randomly change the cutoff + * s("bd sd,hh*4").cutoff(perlin.range(500,2000)).out() + * + */ export const perlin = perlinWith(time); Pattern.prototype._degradeByWith = function (withPat, x) { return this.fmap((a) => (_) => a).appLeft(withPat._filterValues((v) => v > x)); }; +/** + * Randomly removes events from the pattern by a given amount. + * 0 = 0% chance of removal + * 1 = 100% chance of removal + * + * @name degradeBy + * @memberof Pattern + * @param {number} amount - a number between 0 and 1 + * @returns Pattern + * @example + * s("hh*8").degradeBy(0.2).out() + * @example + * s("[hh?0.2]*8").out() + */ Pattern.prototype._degradeBy = function (x) { return this._degradeByWith(rand, x); }; +/** + * + * Randomly removes 50% of events from the pattern. Shorthand for `.degradeBy(0.5)` + * + * @name degrade + * @memberof Pattern + * @returns Pattern + * @example + * s("hh*8").degrade().out() + * @example + * s("[hh?]*8").out() + */ Pattern.prototype.degrade = function () { return this._degradeBy(0.5); }; +/** + * Inverse of {@link Pattern#degradeBy}: Randomly removes events from the pattern by a given amount. + * 0 = 100% chance of removal + * 1 = 0% chance of removal + * Events that would be removed by degradeBy are let through by undegradeBy and vice versa (see second example). + * + * @name undegradeBy + * @memberof Pattern + * @param {number} amount - a number between 0 and 1 + * @returns Pattern + * @example + * s("hh*8").undegradeBy(0.2).out() + */ Pattern.prototype._undegradeBy = function (x) { return this._degradeByWith( rand.fmap((r) => 1 - r), @@ -246,6 +323,25 @@ Pattern.prototype._sometimesBy = function (x, func) { return stack(this._degradeBy(x), func(this._undegradeBy(1 - x))); }; +// https://github.com/tidalcycles/strudel/discussions/198 +/* Pattern.prototype._sometimesBy = function (x, other) { + other = typeof other === 'function' ? other(this._undegradeBy(1 - x)) : reify(other)._undegradeBy(1 - x); + return stack(this._degradeBy(x), other); +}; */ + +/** + * + * Randomly applies the given function by the given probability. + * Similar to {@link Pattern#someCyclesBy} + * + * @name sometimesBy + * @memberof Pattern + * @param {number | Pattern} probability - a number between 0 and 1 + * @param {function} function - the transformation to apply + * @returns Pattern + * @example + * s("hh(3,8)").sometimesBy(.4, x=>x.speed("0.5")).out() + */ Pattern.prototype.sometimesBy = function (patx, func) { const pat = this; return reify(patx) @@ -253,6 +349,7 @@ Pattern.prototype.sometimesBy = function (patx, func) { .innerJoin(); }; +// why does this exist? it is identical to sometimesBy Pattern.prototype._sometimesByPre = function (x, func) { return stack(this._degradeBy(x), func(this).undegradeBy(1 - x)); }; @@ -264,6 +361,17 @@ Pattern.prototype.sometimesByPre = function (patx, func) { .innerJoin(); }; +/** + * + * Applies the given function with a 50% chance + * + * @name sometimes + * @memberof Pattern + * @param {function} function - the transformation to apply + * @returns Pattern + * @example + * s("hh*4").sometimes(x=>x.speed("0.5")).out() + */ Pattern.prototype.sometimes = function (func) { return this._sometimesBy(0.5, func); }; @@ -279,6 +387,19 @@ Pattern.prototype._someCyclesBy = function (x, func) { ); }; +/** + * + * Randomly applies the given function by the given probability on a cycle by cycle basis. + * Similar to {@link Pattern#sometimesBy} + * + * @name someCyclesBy + * @memberof Pattern + * @param {number | Pattern} probability - a number between 0 and 1 + * @param {function} function - the transformation to apply + * @returns Pattern + * @example + * s("hh(3,8)").someCyclesBy(.3, x=>x.speed("0.5")).out() + */ Pattern.prototype.someCyclesBy = function (patx, func) { const pat = this; return reify(patx) @@ -286,30 +407,100 @@ Pattern.prototype.someCyclesBy = function (patx, func) { .innerJoin(); }; +/** + * + * Shorthand for `.someCyclesBy(0.5, fn)` + * + * @name someCycles + * @memberof Pattern + * @returns Pattern + * @example + * s("hh(3,8)").someCycles(x=>x.speed("0.5")).out() + */ Pattern.prototype.someCycles = function (func) { return this._someCyclesBy(0.5, func); }; +/** + * + * Shorthand for `.sometimesBy(0.75, fn)` + * + * @name often + * @memberof Pattern + * @returns Pattern + * @example + * s("hh*8").often(x=>x.speed("0.5")).out() + */ Pattern.prototype.often = function (func) { return this.sometimesBy(0.75, func); }; +/** + * + * Shorthand for `.sometimesBy(0.25, fn)` + * + * @name rarely + * @memberof Pattern + * @returns Pattern + * @example + * s("hh*8").rarely(x=>x.speed("0.5")).out() + */ Pattern.prototype.rarely = function (func) { return this.sometimesBy(0.25, func); }; +/** + * + * Shorthand for `.sometimesBy(0.1, fn)` + * + * @name almostNever + * @memberof Pattern + * @returns Pattern + * @example + * s("hh*8").almostNever(x=>x.speed("0.5")).out() + */ Pattern.prototype.almostNever = function (func) { return this.sometimesBy(0.1, func); }; +/** + * + * Shorthand for `.sometimesBy(0.9, fn)` + * + * @name almostAlways + * @memberof Pattern + * @returns Pattern + * @example + * s("hh*8").almostAlways(x=>x.speed("0.5")).out() + */ Pattern.prototype.almostAlways = function (func) { return this.sometimesBy(0.9, func); }; +/** + * + * Shorthand for `.sometimesBy(0, fn)` (never calls fn) + * + * @name never + * @memberof Pattern + * @returns Pattern + * @example + * s("hh*8").never(x=>x.speed("0.5")).out() + */ Pattern.prototype.never = function (func) { return this; }; +/** + * + * Shorthand for `.sometimesBy(1, fn)` (always calls fn) + * + * @name always + * @memberof Pattern + * @returns Pattern + * @example + * s("hh*8").always(x=>x.speed("0.5")).out() + */ Pattern.prototype.always = function (func) { return func(this); }; diff --git a/packages/react/vite.config.js b/packages/react/vite.config.js index d51ef97a..6786d022 100644 --- a/packages/react/vite.config.js +++ b/packages/react/vite.config.js @@ -38,7 +38,9 @@ export default defineConfig({ '@codemirror/commands', '@lezer/highlight', '@codemirror/language', - '@uiw/codemirror-themes' + '@uiw/codemirror-themes', + '@uiw/react-codemirror', + '@lezer/highlight', ], }, target: 'esnext', diff --git a/packages/webaudio/sampler.mjs b/packages/webaudio/sampler.mjs index 10c6d6be..a86d26ce 100644 --- a/packages/webaudio/sampler.mjs +++ b/packages/webaudio/sampler.mjs @@ -82,14 +82,14 @@ export const loadGithubSamples = async (path, nameFn) => { }; /** - * load the given sample map for webdirt + * Loads a collection of samples to use with `s` * * @example - * loadSamples({ - * bd: '808bd/BD0000.WAV', - * sd: ['808sd/SD0000.WAV','808sd/SD0010.WAV','808sd/SD0050.WAV'] + * samples({ + * bd: '808bd/BD0000.WAV', + * sd: '808sd/SD0010.WAV' * }, 'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/'); - * s("bd ").n(2).webdirt() + * s("[bd ~]*2, [~ hh]*2, ~ sd").out() * */ diff --git a/packages/webaudio/vowel.mjs b/packages/webaudio/vowel.mjs new file mode 100644 index 00000000..25357642 --- /dev/null +++ b/packages/webaudio/vowel.mjs @@ -0,0 +1,38 @@ +// credits to webdirt: https://github.com/dktr0/WebDirt/blob/41342e81d6ad694a2310d491fef7b7e8b0929efe/js-src/Graph.js#L597 +export var vowelFormant = { + a: { freqs: [660, 1120, 2750, 3000, 3350], gains: [1, 0.5012, 0.0708, 0.0631, 0.0126], qs: [80, 90, 120, 130, 140] }, + e: { freqs: [440, 1800, 2700, 3000, 3300], gains: [1, 0.1995, 0.1259, 0.1, 0.1], qs: [70, 80, 100, 120, 120] }, + i: { freqs: [270, 1850, 2900, 3350, 3590], gains: [1, 0.0631, 0.0631, 0.0158, 0.0158], qs: [40, 90, 100, 120, 120] }, + o: { freqs: [430, 820, 2700, 3000, 3300], gains: [1, 0.3162, 0.0501, 0.0794, 0.01995], qs: [40, 80, 100, 120, 120] }, + u: { freqs: [370, 630, 2750, 3000, 3400], gains: [1, 0.1, 0.0708, 0.0316, 0.01995], qs: [40, 60, 100, 120, 120] }, +}; +if (typeof GainNode !== 'undefined') { + class VowelNode extends GainNode { + constructor(ac, letter) { + super(ac); + if (!vowelFormant[letter]) { + throw new Error('vowel: unknown vowel ' + letter); + } + const { gains, qs, freqs } = vowelFormant[letter]; + const makeupGain = ac.createGain(); + for (let i = 0; i < 5; i++) { + const gain = ac.createGain(); + gain.gain.value = gains[i]; + const filter = ac.createBiquadFilter(); + filter.type = 'bandpass'; + filter.Q.value = qs[i]; + filter.frequency.value = freqs[i]; + this.connect(filter); + filter.connect(gain); + gain.connect(makeupGain); + } + makeupGain.gain.value = 8; // how much makeup gain to add? + this.connect = (target) => makeupGain.connect(target); + return this; + } + } + + AudioContext.prototype.createVowelFilter = function (letter) { + return new VowelNode(this, letter); + }; +} diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 0d683e9c..78256af4 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -9,6 +9,8 @@ import * as strudel from '@strudel.cycles/core'; import { fromMidi, toMidi } from '@strudel.cycles/core'; import { loadBuffer } from './sampler.mjs'; const { Pattern } = strudel; +import './vowel.mjs'; +import workletsUrl from './worklets.mjs?url'; // export const getAudioContext = () => Tone.getContext().rawContext; @@ -19,6 +21,7 @@ export const getAudioContext = () => { } return audioContext; }; + let destination; export const getDestination = () => { const ctx = getAudioContext(); @@ -84,9 +87,13 @@ const getSoundfontKey = (s) => { const getSampleBufferSource = async (s, n, note) => { let transpose = 0; - if (note) { - transpose = toMidi(note) - 36; // C3 is middle C + let midi; + + if (note !== undefined) { + midi = typeof note === 'string' ? toMidi(note) : note; + transpose = midi - 36; // C3 is middle C } + const ac = getAudioContext(); // is sample from loaded samples(..) const samples = getLoadedSamples(); @@ -107,7 +114,7 @@ const getSampleBufferSource = async (s, n, note) => { if (!note) { throw new Error('no note(...) set for sound', s); } - const midiDiff = (noteA) => toMidi(noteA) - toMidi(note); + const midiDiff = (noteA) => toMidi(noteA) - midi; // object format will expect keys as notes const closest = Object.keys(bank) .filter((k) => !k.startsWith('_')) @@ -138,6 +145,31 @@ const splitSN = (s, n) => { return [s2, n2]; }; +let workletsLoading; +function loadWorklets() { + if (workletsLoading) { + return workletsLoading; + } + workletsLoading = getAudioContext().audioWorklet.addModule(workletsUrl); + return workletsLoading; +} + +function getWorklet(ac, processor, params) { + const node = new AudioWorkletNode(ac, processor); + Object.entries(params).forEach(([key, value]) => { + node.parameters.get(key).value = value; + }); + return node; +} + +try { + loadWorklets(); +} catch (err) { + console.warn('could not load AudioWorklet effects coarse, crush and shape', err); +} + +const cutGroups = []; + // export const webaudioOutput = async (t, hap, ct, cps) => { export const webaudioOutput = async (hap, deadline, hapDuration) => { try { @@ -148,7 +180,7 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => { ); } // calculate correct time (tone.js workaround) - const t = ac.currentTime + deadline; + let t = ac.currentTime + deadline; // destructure value let { freq, @@ -164,14 +196,22 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => { hresonance = 1, bandf, bandq = 1, + coarse, + crush, + shape, pan, attack = 0.001, - decay = 0.05, - sustain = 0.5, + decay = 0.001, + sustain = 1, release = 0.001, speed = 1, // sample playback speed begin = 0, end = 1, + vowel, + unit, + nudge = 0, // TODO: is this in seconds? + cut, + loop, } = hap.value; const { velocity = 1 } = hap.context; gain *= velocity; // legacy fix for velocity @@ -185,7 +225,7 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => { } if (!s || ['sine', 'square', 'triangle', 'sawtooth'].includes(s)) { // with synths, n and note are the same thing - n = note || n; + n = note || n || 36; if (typeof n === 'string') { n = toMidi(n); // e.g. c3 => 48 } @@ -239,29 +279,33 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => { return; } bufferSource.playbackRate.value = Math.abs(speed) * bufferSource.playbackRate.value; - // TODO: nudge, unit, cut, loop - let duration = soundfont || clip ? hapDuration : bufferSource.buffer.duration; - // let duration = bufferSource.buffer.duration; - const offset = begin * duration; - duration = ((end - begin) * duration) / Math.abs(speed); - if (soundfont || clip) { - bufferSource.start(t, offset); // duration does not work here for some reason - } else { - bufferSource.start(t, offset, duration); + if (unit === 'c') { + // are there other units? + bufferSource.playbackRate.value = bufferSource.playbackRate.value * bufferSource.buffer.duration; + } + let duration = soundfont || clip ? hapDuration : bufferSource.buffer.duration / bufferSource.playbackRate.value; + // "The computation of the offset into the sound is performed using the sound buffer's natural sample rate, + // rather than the current playback rate, so even if the sound is playing at twice its normal speed, + // the midway point through a 10-second audio buffer is still 5." + const offset = begin * duration * bufferSource.playbackRate.value; + duration = (end - begin) * duration; + if (loop) { + bufferSource.loop = true; + bufferSource.loopStart = offset; + bufferSource.loopEnd = offset + duration; + duration = loop * duration; + } + t += nudge; + + bufferSource.start(t, offset); + if (cut !== undefined) { + cutGroups[cut]?.stop(t); // fade out? + cutGroups[cut] = bufferSource; } chain.push(bufferSource); - if (soundfont || clip) { - const env = ac.createGain(); - const releaseLength = 0.1; - env.gain.value = 0.6; - env.gain.setValueAtTime(env.gain.value, t + duration); - env.gain.linearRampToValueAtTime(0, t + duration + releaseLength); - // env.gain.linearRampToValueAtTime(0, t + duration + releaseLength); - chain.push(env); - bufferSource.stop(t + duration + releaseLength); - } else { - bufferSource.stop(t + duration); - } + bufferSource.stop(t + duration + release); + const adsr = getADSR(attack, decay, sustain, release, 1, t, t + duration); + chain.push(adsr); } // master out const master = ac.createGain(); @@ -272,7 +316,10 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => { cutoff !== undefined && chain.push(getFilter('lowpass', cutoff, resonance)); hcutoff !== undefined && chain.push(getFilter('highpass', hcutoff, hresonance)); bandf !== undefined && chain.push(getFilter('bandpass', bandf, bandq)); - // TODO vowel + vowel !== undefined && chain.push(ac.createVowelFilter(vowel)); + coarse !== undefined && chain.push(getWorklet(ac, 'coarse-processor', { coarse })); + crush !== undefined && chain.push(getWorklet(ac, 'crush-processor', { crush })); + shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape })); // TODO delay / delaytime / delayfeedback // panning if (pan !== undefined) { @@ -280,13 +327,11 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => { panner.pan.value = 2 * pan - 1; chain.push(panner); } - // master out - /* const master = ac.createGain(); - master.gain.value = 0.8 * gain; - chain.push(master); */ - chain.push(getDestination()); // connect chain elements together chain.slice(1).reduce((last, current) => last.connect(current), chain[0]); + chain[chain.length - 1].connect(getDestination()); + // disconnect all nodes when source node has ended: + chain[0].onended = () => chain.forEach((n) => n.disconnect()); } catch (e) { console.warn('.out error:', e); } diff --git a/packages/webaudio/worklets.mjs b/packages/webaudio/worklets.mjs new file mode 100644 index 00000000..c5306c45 --- /dev/null +++ b/packages/webaudio/worklets.mjs @@ -0,0 +1,96 @@ +// LICENSE GNU General Public License v3.0 see https://github.com/dktr0/WebDirt/blob/main/LICENSE +// all the credit goes to dktr0's webdirt: https://github.com/dktr0/WebDirt/blob/5ce3d698362c54d6e1b68acc47eb2955ac62c793/dist/AudioWorklets.js +// <3 + +class CoarseProcessor extends AudioWorkletProcessor { + static get parameterDescriptors() { + return [{ name: 'coarse', defaultValue: 1 }]; + } + + constructor() { + super(); + this.notStarted = true; + } + + process(inputs, outputs, parameters) { + const input = inputs[0]; + const output = outputs[0]; + const coarse = parameters.coarse; + const blockSize = 128; + const hasInput = !(input[0] === undefined); + if (hasInput) { + this.notStarted = false; + output[0][0] = input[0][0]; + for (let n = 1; n < blockSize; n++) { + if (n % coarse == 0) output[0][n] = input[0][n]; + else output[0][n] = output[0][n - 1]; + } + } + return this.notStarted || hasInput; + } +} + +registerProcessor('coarse-processor', CoarseProcessor); + +class CrushProcessor extends AudioWorkletProcessor { + static get parameterDescriptors() { + return [{ name: 'crush', defaultValue: 0 }]; + } + + constructor() { + super(); + this.notStarted = true; + } + + process(inputs, outputs, parameters) { + const input = inputs[0]; + const output = outputs[0]; + const crush = parameters.crush; + const blockSize = 128; + const hasInput = !(input[0] === undefined); + if (hasInput) { + this.notStarted = false; + if (crush.length === 1) { + const x = Math.pow(2, crush[0] - 1); + for (let n = 0; n < blockSize; n++) output[0][n] = Math.round(input[0][n] * x) / x; + } else { + for (let n = 0; n < blockSize; n++) { + let x = Math.pow(2, crush[n] - 1); + output[0][n] = Math.round(input[0][n] * x) / x; + } + } + } + return this.notStarted || hasInput; + } +} +registerProcessor('crush-processor', CrushProcessor); + +class ShapeProcessor extends AudioWorkletProcessor { + static get parameterDescriptors() { + return [{ name: 'shape', defaultValue: 0 }]; + } + + constructor() { + super(); + this.notStarted = true; + } + + process(inputs, outputs, parameters) { + const input = inputs[0]; + const output = outputs[0]; + const shape0 = parameters.shape[0]; + const shape1 = shape0 < 1 ? shape0 : 1.0 - 4e-10; + const shape = (2.0 * shape1) / (1.0 - shape1); + const blockSize = 128; + const hasInput = !(input[0] === undefined); + if (hasInput) { + this.notStarted = false; + for (let n = 0; n < blockSize; n++) { + output[0][n] = ((1 + shape) * input[0][n]) / (1 + shape * Math.abs(input[0][n])); + } + } + return this.notStarted || hasInput; + } +} + +registerProcessor('shape-processor', ShapeProcessor); diff --git a/repl/package-lock.json b/repl/package-lock.json index 10375f90..ba6a8611 100644 --- a/repl/package-lock.json +++ b/repl/package-lock.json @@ -18,6 +18,7 @@ "@vitejs/plugin-react": "^1.3.0", "autoprefixer": "^10.4.7", "postcss": "^8.4.13", + "rollup-plugin-visualizer": "^5.8.1", "tailwindcss": "^3.0.24", "vite": "^2.9.9" } @@ -634,6 +635,15 @@ "node": ">=0.4.0" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -838,6 +848,17 @@ "node": ">= 6" } }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -908,6 +929,15 @@ } } }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/defined": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", @@ -949,6 +979,12 @@ "integrity": "sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==", "dev": true }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/es5-ext": { "version": "0.10.61", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz", @@ -1465,6 +1501,15 @@ "node": ">=6.9.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -1531,6 +1576,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1540,6 +1600,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1566,6 +1635,18 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1753,6 +1834,23 @@ "node": ">= 6" } }, + "node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -1977,6 +2075,15 @@ "node": ">=8.10.0" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", @@ -2019,6 +2126,44 @@ "fsevents": "~2.3.2" } }, + "node_modules/rollup-plugin-visualizer": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.8.1.tgz", + "integrity": "sha512-NBT/xN/LWCwDM2/j5vYmjzpEAKHyclo/8Cv8AfTCwgADAG+tLJDy1vzxMw6NO0dSDjmTeRELD9UU3FwknLv0GQ==", + "dev": true, + "dependencies": { + "nanoid": "^3.3.4", + "open": "^8.4.0", + "source-map": "^0.7.3", + "yargs": "^17.5.1" + }, + "bin": { + "rollup-plugin-visualizer": "dist/bin/cli.js" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "rollup": "^2.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/rollup-plugin-visualizer/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/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -2066,6 +2211,15 @@ "semver": "bin/semver.js" } }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -2075,6 +2229,32 @@ "node": ">=0.10.0" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -2281,6 +2461,56 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -2290,6 +2520,15 @@ "node": ">=0.4" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yaeti": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", @@ -2306,6 +2545,33 @@ "engines": { "node": ">= 6" } + }, + "node_modules/yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } } }, "dependencies": { @@ -2776,6 +3042,12 @@ "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", "dev": true }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -2901,6 +3173,17 @@ } } }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -2957,6 +3240,12 @@ "ms": "2.1.2" } }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, "defined": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", @@ -2992,6 +3281,12 @@ "integrity": "sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==", "dev": true }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "es5-ext": { "version": "0.10.61", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz", @@ -3289,6 +3584,12 @@ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, "glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -3337,12 +3638,24 @@ "has": "^1.0.3" } }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -3363,6 +3676,15 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3492,6 +3814,17 @@ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "dev": true }, + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -3619,6 +3952,12 @@ "picomatch": "^2.2.1" } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, "resolve": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", @@ -3645,6 +3984,26 @@ "fsevents": "~2.3.2" } }, + "rollup-plugin-visualizer": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.8.1.tgz", + "integrity": "sha512-NBT/xN/LWCwDM2/j5vYmjzpEAKHyclo/8Cv8AfTCwgADAG+tLJDy1vzxMw6NO0dSDjmTeRELD9UU3FwknLv0GQ==", + "dev": true, + "requires": { + "nanoid": "^3.3.4", + "open": "^8.4.0", + "source-map": "^0.7.3", + "yargs": "^17.5.1" + }, + "dependencies": { + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true + } + } + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -3675,12 +4034,38 @@ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true + }, "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 }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -3835,12 +4220,55 @@ "webidl-conversions": "^3.0.0" } }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "dev": true }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, "yaeti": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", @@ -3851,6 +4279,27 @@ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", "dev": true + }, + "yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true } } } diff --git a/repl/package.json b/repl/package.json index a15f8339..1ee3983e 100644 --- a/repl/package.json +++ b/repl/package.json @@ -25,6 +25,7 @@ "@vitejs/plugin-react": "^1.3.0", "autoprefixer": "^10.4.7", "postcss": "^8.4.13", + "rollup-plugin-visualizer": "^5.8.1", "tailwindcss": "^3.0.24", "vite": "^2.9.9" } diff --git a/repl/src/App.jsx b/repl/src/App.jsx index cf760100..ccec1f3c 100644 --- a/repl/src/App.jsx +++ b/repl/src/App.jsx @@ -219,6 +219,19 @@ function App() { <>loading... )} + {!isEmbedded && (