diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index 4470173b..512c873a 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -12,7 +12,8 @@ import { lineNumbers, drawSelection, } from '@codemirror/view'; -import { Pattern, Drawer, repl, cleanupDraw } from '@strudel/core'; +import { Pattern, repl } from '@strudel/core'; +import { Drawer, cleanupDraw } from '@strudel/draw'; import { isAutoCompletionEnabled } from './autocomplete.mjs'; import { isTooltipEnabled } from './tooltip.mjs'; import { flash, isFlashEnabled } from './flash.mjs'; diff --git a/packages/codemirror/package.json b/packages/codemirror/package.json index 22213ac7..0b39ebdd 100644 --- a/packages/codemirror/package.json +++ b/packages/codemirror/package.json @@ -45,6 +45,7 @@ "@replit/codemirror-vim": "^6.1.0", "@replit/codemirror-vscode-keymap": "^6.0.2", "@strudel/core": "workspace:*", + "@strudel/draw": "workspace:*", "@uiw/codemirror-themes": "^4.21.21", "@uiw/codemirror-themes-all": "^4.21.21", "nanostores": "^0.9.5" diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 9a236d89..cb75915a 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1277,7 +1277,10 @@ export const { roomsize, size, sz, rsize } = registerControl('roomsize', 'size', // ['sclaves'], // ['scrash'], /** - * Wave shaping distortion. CAUTION: it might get loud + * (Deprecated) Wave shaping distortion. WARNING: can suddenly get unpredictably loud. + * Please use distort instead, which has a more predictable response curve + * second option in optional array syntax (ex: ".9:.5") applies a postgain to the output + * * * @name shape * @param {number | Pattern} distortion between 0 and 1 @@ -1285,7 +1288,22 @@ export const { roomsize, size, sz, rsize } = registerControl('roomsize', 'size', * s("bd sd [~ bd] sd,hh*8").shape("<0 .2 .4 .6 .8>") * */ -export const { shape } = registerControl('shape'); +export const { shape } = registerControl(['shape', 'shapevol']); +/** + * Wave shaping distortion. CAUTION: it can get loud. + * Second option in optional array syntax (ex: ".9:.5") applies a postgain to the output. + * Most useful values are usually between 0 and 10 (depending on source gain). If you are feeling adventurous, you can turn it up to 11 and beyond ;) + * + * @name distort + * @synonyms dist + * @param {number | Pattern} distortion + * @example + * s("bd sd [~ bd] sd,hh*8").distort("<0 2 3 10:.5>") + * @example + * note("d1!8").s("sine").penv(36).pdecay(.12).decay(.23).distort("8:.4") + * + */ +export const { distort, dist } = registerControl(['distort', 'distortvol'], 'dist'); /** * Dynamics Compressor. The params are `compressor("threshold:ratio:knee:attack:release")` * More info [here](https://developer.mozilla.org/en-US/docs/Web/API/DynamicsCompressorNode?retiredLocale=de#instance_properties) @@ -1419,7 +1437,6 @@ export const { octersubsub } = registerControl('octersubsub'); export const { ring } = registerControl('ring'); export const { ringf } = registerControl('ringf'); export const { ringdf } = registerControl('ringdf'); -export const { distort } = registerControl('distort'); export const { freeze } = registerControl('freeze'); export const { xsdelay } = registerControl('xsdelay'); export const { tsdelay } = registerControl('tsdelay'); diff --git a/packages/core/index.mjs b/packages/core/index.mjs index 9d998349..a04c85bc 100644 --- a/packages/core/index.mjs +++ b/packages/core/index.mjs @@ -22,10 +22,6 @@ export * from './repl.mjs'; export * from './cyclist.mjs'; export * from './logger.mjs'; export * from './time.mjs'; -export * from './draw.mjs'; -export * from './animate.mjs'; -export * from './pianoroll.mjs'; -export * from './spiral.mjs'; export * from './ui.mjs'; export { default as drawLine } from './drawLine.mjs'; // below won't work with runtime.mjs (json import fails) diff --git a/packages/draw/README.md b/packages/draw/README.md new file mode 100644 index 00000000..e144d85b --- /dev/null +++ b/packages/draw/README.md @@ -0,0 +1,9 @@ +# @strudel/canvas + +Helpers for drawing with the Canvas API and Strudel + +## Install + +```sh +npm i @strudel/canvas --save +``` diff --git a/packages/core/animate.mjs b/packages/draw/animate.mjs similarity index 89% rename from packages/core/animate.mjs rename to packages/draw/animate.mjs index cc7e59b2..d8508151 100644 --- a/packages/core/animate.mjs +++ b/packages/draw/animate.mjs @@ -1,11 +1,14 @@ -import { Pattern, getDrawContext, silence, register, pure, createParams } from './index.mjs'; +import { Pattern, silence, register, pure, createParams } from '@strudel/core'; +import { getDrawContext } from './draw.mjs'; let clearColor = '#22222210'; Pattern.prototype.animate = function ({ callback, sync = false, smear = 0.5 } = {}) { window.frame && cancelAnimationFrame(window.frame); const ctx = getDrawContext(); - const { clientWidth: ww, clientHeight: wh } = ctx.canvas; + let { clientWidth: ww, clientHeight: wh } = ctx.canvas; + ww *= window.devicePixelRatio; + wh *= window.devicePixelRatio; let smearPart = smear === 0 ? '99' : Number((1 - smear) * 100).toFixed(0); smearPart = smearPart.length === 1 ? `0${smearPart}` : smearPart; clearColor = `#200010${smearPart}`; diff --git a/packages/core/color.mjs b/packages/draw/color.mjs similarity index 100% rename from packages/core/color.mjs rename to packages/draw/color.mjs diff --git a/packages/core/draw.mjs b/packages/draw/draw.mjs similarity index 82% rename from packages/core/draw.mjs rename to packages/draw/draw.mjs index 30de7ba8..2ea531cf 100644 --- a/packages/core/draw.mjs +++ b/packages/draw/draw.mjs @@ -1,31 +1,32 @@ /* draw.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -import { Pattern, getTime, State, TimeSpan } from './index.mjs'; +import { Pattern, getTime, State, TimeSpan } from '@strudel/core'; -export const getDrawContext = (id = 'test-canvas') => { +export const getDrawContext = (id = 'test-canvas', options) => { + let { contextType = '2d', pixelated = false, pixelRatio = window.devicePixelRatio } = options || {}; let canvas = document.querySelector('#' + id); if (!canvas) { - const scale = 2; // 2 = crisp on retina screens canvas = document.createElement('canvas'); canvas.id = id; - canvas.width = window.innerWidth * scale; - canvas.height = window.innerHeight * scale; + canvas.width = window.innerWidth * pixelRatio; + canvas.height = window.innerHeight * pixelRatio; canvas.style = 'pointer-events:none;width:100%;height:100%;position:fixed;top:0;left:0'; + pixelated && (canvas.style.imageRendering = 'pixelated'); document.body.prepend(canvas); let timeout; window.addEventListener('resize', () => { timeout && clearTimeout(timeout); timeout = setTimeout(() => { - canvas.width = window.innerWidth * scale; - canvas.height = window.innerHeight * scale; + canvas.width = window.innerWidth * pixelRatio; + canvas.height = window.innerHeight * pixelRatio; }, 200); }); } - return canvas.getContext('2d'); + return canvas.getContext(contextType); }; Pattern.prototype.draw = function (callback, { from, to, onQuery } = {}) { @@ -61,6 +62,25 @@ Pattern.prototype.draw = function (callback, { from, to, onQuery } = {}) { return this; }; +// this is a more generic helper to get a rendering callback for the currently active haps +// TODO: this misses events that are prolonged with clip or duration (would need state) +Pattern.prototype.onFrame = function (fn, offset = 0) { + if (typeof window === 'undefined') { + return this; + } + if (window.strudelAnimation) { + cancelAnimationFrame(window.strudelAnimation); + } + const animate = () => { + const t = getTime() + offset; + const haps = this.queryArc(t, t); + fn(haps, t, this); + window.strudelAnimation = requestAnimationFrame(animate); + }; + requestAnimationFrame(animate); + return this; +}; + export const cleanupDraw = (clearScreen = true) => { const ctx = getDrawContext(); clearScreen && ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.width); diff --git a/packages/draw/index.mjs b/packages/draw/index.mjs new file mode 100644 index 00000000..89cda805 --- /dev/null +++ b/packages/draw/index.mjs @@ -0,0 +1,5 @@ +export * from './animate.mjs'; +export * from './color.mjs'; +export * from './draw.mjs'; +export * from './pianoroll.mjs'; +export * from './spiral.mjs'; diff --git a/packages/draw/package.json b/packages/draw/package.json new file mode 100644 index 00000000..346a4723 --- /dev/null +++ b/packages/draw/package.json @@ -0,0 +1,37 @@ +{ + "name": "@strudel/draw", + "version": "1.0.1", + "description": "Helpers for drawing with Strudel", + "main": "index.mjs", + "type": "module", + "publishConfig": { + "main": "dist/index.mjs" + }, + "scripts": { + "build": "vite build", + "prepublishOnly": "npm run build" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/tidalcycles/strudel.git" + }, + "keywords": [ + "titdalcycles", + "strudel", + "pattern", + "livecoding", + "algorave" + ], + "author": "Felix Roos ", + "license": "AGPL-3.0-or-later", + "bugs": { + "url": "https://github.com/tidalcycles/strudel/issues" + }, + "homepage": "https://github.com/tidalcycles/strudel#readme", + "dependencies": { + "@strudel/core": "workspace:*" + }, + "devDependencies": { + "vite": "^5.0.10" + } +} diff --git a/packages/core/pianoroll.mjs b/packages/draw/pianoroll.mjs similarity index 98% rename from packages/core/pianoroll.mjs rename to packages/draw/pianoroll.mjs index 2f8c9fca..74b1480e 100644 --- a/packages/core/pianoroll.mjs +++ b/packages/draw/pianoroll.mjs @@ -1,10 +1,10 @@ /* pianoroll.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -import { Pattern, noteToMidi, getDrawContext, freqToMidi, isNote } from './index.mjs'; +import { Pattern, noteToMidi, freqToMidi } from '@strudel/core'; const scale = (normalized, min, max) => normalized * (max - min) + min; const getValue = (e) => { diff --git a/packages/core/spiral.mjs b/packages/draw/spiral.mjs similarity index 98% rename from packages/core/spiral.mjs rename to packages/draw/spiral.mjs index e0d5cd87..00bd62ec 100644 --- a/packages/core/spiral.mjs +++ b/packages/draw/spiral.mjs @@ -1,4 +1,4 @@ -import { Pattern } from './index.mjs'; +import { Pattern } from '@strudel/core'; // polar coords -> xy function fromPolar(angle, radius, cx, cy) { diff --git a/packages/draw/vite.config.js b/packages/draw/vite.config.js new file mode 100644 index 00000000..5df3edc1 --- /dev/null +++ b/packages/draw/vite.config.js @@ -0,0 +1,19 @@ +import { defineConfig } from 'vite'; +import { dependencies } from './package.json'; +import { resolve } from 'path'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [], + build: { + lib: { + entry: resolve(__dirname, 'index.mjs'), + formats: ['es'], + fileName: (ext) => ({ es: 'index.mjs' })[ext], + }, + rollupOptions: { + external: [...Object.keys(dependencies)], + }, + target: 'esnext', + }, +}); diff --git a/packages/hydra/hydra.mjs b/packages/hydra/hydra.mjs index dc1ba3f9..b25c7c24 100644 --- a/packages/hydra/hydra.mjs +++ b/packages/hydra/hydra.mjs @@ -1,15 +1,8 @@ -import { getDrawContext } from '@strudel/core'; +import { getDrawContext } from '@strudel/draw'; +import { controls } from '@strudel/core'; let latestOptions; - -function appendCanvas(c) { - const { canvas: testCanvas } = getDrawContext(); - c.canvas.id = 'hydra-canvas'; - c.canvas.style.position = 'fixed'; - c.canvas.style.top = '0px'; - testCanvas.after(c.canvas); - return testCanvas; -} +let hydra; export async function initHydra(options = {}) { // reset if options have changed since last init @@ -19,22 +12,38 @@ export async function initHydra(options = {}) { latestOptions = options; //load and init hydra if (!document.getElementById('hydra-canvas')) { - console.log('reinit..'); const { src = 'https://unpkg.com/hydra-synth', feedStrudel = false, + contextType = 'webgl', + pixelRatio = 1, + pixelated = true, ...hydraConfig - } = { detectAudio: false, ...options }; + } = { + detectAudio: false, + ...options, + }; + const { canvas } = getDrawContext('hydra-canvas', { contextType, pixelRatio, pixelated }); + hydraConfig.canvas = canvas; await import(/* @vite-ignore */ src); - const hydra = new Hydra(hydraConfig); + hydra = new Hydra(hydraConfig); if (feedStrudel) { const { canvas } = getDrawContext(); canvas.style.display = 'none'; hydra.synth.s0.init({ src: canvas }); } - appendCanvas(hydra); } } +export function clearHydra() { + if (hydra) { + hydra.hush(); + } + globalThis.s0?.clear(); + document.getElementById('hydra-canvas')?.remove(); + globalThis.speed = controls.speed; + globalThis.shape = controls.shape; +} + export const H = (p) => () => p.queryArc(getTime(), getTime())[0].value; diff --git a/packages/hydra/package.json b/packages/hydra/package.json index 48189663..77fab126 100644 --- a/packages/hydra/package.json +++ b/packages/hydra/package.json @@ -34,6 +34,7 @@ "homepage": "https://github.com/tidalcycles/strudel#readme", "dependencies": { "@strudel/core": "workspace:*", + "@strudel/draw": "workspace:*", "hydra-synth": "^1.3.29" }, "devDependencies": { diff --git a/packages/mini/mini.mjs b/packages/mini/mini.mjs index 9217ae24..fdd3d3b1 100644 --- a/packages/mini/mini.mjs +++ b/packages/mini/mini.mjs @@ -125,7 +125,7 @@ export function patternifyAST(ast, code, onEnter, offset = 0) { return enter(ast.source_); } case 'atom': { - if (ast.source_ === '~') { + if (ast.source_ === '~' || ast.source_ === '-') { return strudel.silence; } if (!ast.location_) { diff --git a/packages/mini/test/mini.test.mjs b/packages/mini/test/mini.test.mjs index 6bf1bbce..647512a8 100644 --- a/packages/mini/test/mini.test.mjs +++ b/packages/mini/test/mini.test.mjs @@ -117,6 +117,9 @@ describe('mini', () => { checkEuclid([11, 24], 'x ~ ~ x ~ x ~ x ~ x ~ x ~ ~ x ~ x ~ x ~ x ~ x ~'); checkEuclid([13, 24], 'x ~ x x ~ x ~ x ~ x ~ x ~ x x ~ x ~ x ~ x ~ x ~'); }); + it('supports the - alias for ~', () => { + expect(minS('a - b [- c]')).toEqual(minS('a ~ b [~ c]')); + }); it('supports the ? operator', () => { expect( mini('a?') diff --git a/packages/repl/package.json b/packages/repl/package.json index e6bce6d5..70687d1c 100644 --- a/packages/repl/package.json +++ b/packages/repl/package.json @@ -35,6 +35,7 @@ "dependencies": { "@strudel/codemirror": "workspace:*", "@strudel/core": "workspace:*", + "@strudel/draw": "workspace:*", "@strudel/hydra": "workspace:*", "@strudel/midi": "workspace:*", "@strudel/mini": "workspace:*", diff --git a/packages/repl/prebake.mjs b/packages/repl/prebake.mjs index 4315b85c..9fc1c881 100644 --- a/packages/repl/prebake.mjs +++ b/packages/repl/prebake.mjs @@ -6,6 +6,7 @@ export async function prebake() { const modulesLoading = evalScope( // import('@strudel/core'), core, + import('@strudel/draw'), import('@strudel/mini'), import('@strudel/tonal'), import('@strudel/webaudio'), diff --git a/packages/repl/repl-component.mjs b/packages/repl/repl-component.mjs index 7928ac43..4fa8d6d2 100644 --- a/packages/repl/repl-component.mjs +++ b/packages/repl/repl-component.mjs @@ -1,4 +1,5 @@ -import { getDrawContext, silence } from '@strudel/core'; +import { silence } from '@strudel/core'; +import { getDrawContext } from '@strudel/draw'; import { transpiler } from '@strudel/transpiler'; import { getAudioContext, webaudioOutput } from '@strudel/webaudio'; import { StrudelMirror, codemirrorSettings } from '@strudel/codemirror'; diff --git a/packages/superdough/README.md b/packages/superdough/README.md index b1ccae60..0c6e4f14 100644 --- a/packages/superdough/README.md +++ b/packages/superdough/README.md @@ -65,7 +65,7 @@ superdough({ s: 'bd', delay: 0.5 }, 0, 1); - `bandf`: band pass filter cutoff - `bandq`: band pass filter resonance - `crush`: amplitude bit crusher using given number of bits - - `shape`: distortion effect from 0 (none) to 1 (full). might get loud! + - `distort`: distortion effect. might get loud! - `pan`: stereo panning from 0 (left) to 1 (right) - `phaser`: sets the speed of the modulation - `phaserdepth`: the amount the signal is affected by the phaser effect. diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 95f1cc53..3e6448d9 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -316,6 +316,9 @@ export const superdough = async (value, deadline, hapDuration) => { coarse, crush, shape, + shapevol = 1, + distort, + distortvol = 1, pan, vowel, delay = 0, @@ -457,7 +460,8 @@ export const superdough = async (value, deadline, hapDuration) => { // effects 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 })); + shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape, postgain: shapevol })); + distort !== undefined && chain.push(getWorklet(ac, 'distort-processor', { distort, postgain: distortvol })); compressorThreshold !== undefined && chain.push( diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 7bb43f87..4f2965ee 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -1,6 +1,5 @@ +// coarse, crush, and shape processors adapted from dktr0's webdirt: https://github.com/dktr0/WebDirt/blob/5ce3d698362c54d6e1b68acc47eb2955ac62c793/dist/AudioWorklets.js // 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() { @@ -9,28 +8,27 @@ class CoarseProcessor extends AudioWorkletProcessor { 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++) { - for (let o = 0; o < output.length; o++) { - output[o][n] = n % coarse == 0 ? input[0][n] : output[o][n - 1]; - } + + let coarse = parameters.coarse[0] ?? 0; + coarse = Math.max(1, coarse); + + if (input[0] == null || output[0] == null) { + return false; + } + for (let n = 0; n < blockSize; n++) { + for (let i = 0; i < input.length; i++) { + output[i][n] = n % coarse === 0 ? input[i][n] : output[i][n - 1]; } } - return this.notStarted || hasInput; + return true; } } - registerProcessor('coarse-processor', CoarseProcessor); class CrushProcessor extends AudioWorkletProcessor { @@ -40,69 +38,94 @@ class CrushProcessor extends AudioWorkletProcessor { 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++) { - const value = Math.round(input[0][n] * x) / x; - for (let o = 0; o < output.length; o++) { - output[o][n] = value; - } - } - } else { - for (let n = 0; n < blockSize; n++) { - let x = Math.pow(2, crush[n] - 1); - const value = Math.round(input[0][n] * x) / x; - for (let o = 0; o < output.length; o++) { - output[o][n] = value; - } - } + + let crush = parameters.crush[0] ?? 8; + crush = Math.max(1, crush); + + if (input[0] == null || output[0] == null) { + return false; + } + for (let n = 0; n < blockSize; n++) { + for (let i = 0; i < input.length; i++) { + const x = Math.pow(2, crush - 1); + output[i][n] = Math.round(input[i][n] * x) / x; } } - return this.notStarted || hasInput; + return true; } } registerProcessor('crush-processor', CrushProcessor); class ShapeProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { - return [{ name: 'shape', defaultValue: 0 }]; + return [ + { name: 'shape', defaultValue: 0 }, + { name: 'postgain', defaultValue: 1 }, + ]; } 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++) { - const value = ((1 + shape) * input[0][n]) / (1 + shape * Math.abs(input[0][n])); - for (let o = 0; o < output.length; o++) { - output[o][n] = value; - } + + let shape = parameters.shape[0]; + shape = shape < 1 ? shape : 1.0 - 4e-10; + shape = (2.0 * shape) / (1.0 - shape); + const postgain = Math.max(0.001, Math.min(1, parameters.postgain[0])); + + if (input[0] == null || output[0] == null) { + return false; + } + for (let n = 0; n < blockSize; n++) { + for (let i = 0; i < input.length; i++) { + output[i][n] = (((1 + shape) * input[i][n]) / (1 + shape * Math.abs(input[i][n]))) * postgain; } } - return this.notStarted || hasInput; + return true; } } - registerProcessor('shape-processor', ShapeProcessor); + +class DistortProcessor extends AudioWorkletProcessor { + static get parameterDescriptors() { + return [ + { name: 'distort', defaultValue: 0 }, + { name: 'postgain', defaultValue: 1 }, + ]; + } + + constructor() { + super(); + } + + process(inputs, outputs, parameters) { + const input = inputs[0]; + const output = outputs[0]; + const blockSize = 128; + + const shape = Math.expm1(parameters.distort[0]); + const postgain = Math.max(0.001, Math.min(1, parameters.postgain[0])); + + if (input[0] == null || output[0] == null) { + return false; + } + for (let n = 0; n < blockSize; n++) { + for (let i = 0; i < input.length; i++) { + output[i][n] = (((1 + shape) * input[i][n]) / (1 + shape * Math.abs(input[i][n]))) * postgain; + } + } + return true; + } +} +registerProcessor('distort-processor', DistortProcessor); diff --git a/packages/webaudio/package.json b/packages/webaudio/package.json index dce53961..f9cd7cc7 100644 --- a/packages/webaudio/package.json +++ b/packages/webaudio/package.json @@ -34,6 +34,7 @@ "homepage": "https://github.com/tidalcycles/strudel#readme", "dependencies": { "@strudel/core": "workspace:*", + "@strudel/draw": "workspace:*", "superdough": "workspace:*" }, "devDependencies": { diff --git a/packages/webaudio/scope.mjs b/packages/webaudio/scope.mjs index 115f159a..0371366c 100644 --- a/packages/webaudio/scope.mjs +++ b/packages/webaudio/scope.mjs @@ -1,4 +1,5 @@ -import { Pattern, getDrawContext, clamp } from '@strudel/core'; +import { Pattern, clamp } from '@strudel/core'; +import { getDrawContext } from '../draw/index.mjs'; import { analyser, getAnalyzerData } from 'superdough'; export function drawTimeScope( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4b31cc8b..b123e2b7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -96,7 +96,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 examples/headless-repl: dependencies: @@ -106,7 +106,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 examples/minimal-repl: dependencies: @@ -128,7 +128,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 examples/superdough: dependencies: @@ -138,7 +138,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/codemirror: dependencies: @@ -181,6 +181,9 @@ importers: '@strudel/core': specifier: workspace:* version: link:../core + '@strudel/draw': + specifier: workspace:* + version: link:../draw '@uiw/codemirror-themes': specifier: ^4.21.21 version: 4.21.21(@codemirror/language@6.10.0)(@codemirror/state@6.4.0)(@codemirror/view@6.23.0) @@ -193,7 +196,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/core: dependencies: @@ -203,7 +206,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 vitest: specifier: ^1.1.0 version: 1.1.0(@vitest/ui@1.1.0) @@ -222,7 +225,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/desktopbridge: dependencies: @@ -233,6 +236,16 @@ importers: specifier: ^1.5.3 version: 1.5.3 + packages/draw: + dependencies: + '@strudel/core': + specifier: workspace:* + version: link:../core + devDependencies: + vite: + specifier: ^5.0.10 + version: 5.0.11(@types/node@20.10.6) + packages/embed: {} packages/hydra: @@ -240,6 +253,9 @@ importers: '@strudel/core': specifier: workspace:* version: link:../core + '@strudel/draw': + specifier: workspace:* + version: link:../draw hydra-synth: specifier: ^1.3.29 version: 1.3.29 @@ -249,7 +265,7 @@ importers: version: 5.8.1 vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/midi: dependencies: @@ -265,7 +281,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/mini: dependencies: @@ -278,7 +294,7 @@ importers: version: 3.0.2 vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 vitest: specifier: ^1.1.0 version: 1.1.0(@vitest/ui@1.1.0) @@ -297,7 +313,7 @@ importers: version: 5.8.1 vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/repl: dependencies: @@ -307,6 +323,9 @@ importers: '@strudel/core': specifier: workspace:* version: link:../core + '@strudel/draw': + specifier: workspace:* + version: link:../draw '@strudel/hydra': specifier: workspace:* version: link:../hydra @@ -337,7 +356,7 @@ importers: version: 5.12.0 vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/serial: dependencies: @@ -347,7 +366,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/soundfonts: dependencies: @@ -369,7 +388,7 @@ importers: version: 3.3.2 vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/superdough: dependencies: @@ -379,7 +398,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/tonal: dependencies: @@ -398,7 +417,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 vitest: specifier: ^1.1.0 version: 1.1.0(@vitest/ui@1.1.0) @@ -423,7 +442,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 vitest: specifier: ^1.1.0 version: 1.1.0(@vitest/ui@1.1.0) @@ -451,20 +470,23 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/webaudio: dependencies: '@strudel/core': specifier: workspace:* version: link:../core + '@strudel/draw': + specifier: workspace:* + version: link:../draw superdough: specifier: workspace:* version: link:../superdough devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/xen: dependencies: @@ -474,7 +496,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 vitest: specifier: ^1.1.0 version: 1.1.0(@vitest/ui@1.1.0) @@ -535,6 +557,9 @@ importers: '@strudel/desktopbridge': specifier: workspace:* version: link:../packages/desktopbridge + '@strudel/draw': + specifier: workspace:* + version: link:../packages/draw '@strudel/hydra': specifier: workspace:* version: link:../packages/hydra @@ -2382,6 +2407,7 @@ packages: cpu: [arm64] os: [android] requiresBuild: true + dev: true optional: true /@esbuild/android-arm@0.19.11: @@ -2398,6 +2424,7 @@ packages: cpu: [arm] os: [android] requiresBuild: true + dev: true optional: true /@esbuild/android-x64@0.19.11: @@ -2414,6 +2441,7 @@ packages: cpu: [x64] os: [android] requiresBuild: true + dev: true optional: true /@esbuild/darwin-arm64@0.19.11: @@ -2430,6 +2458,7 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true + dev: true optional: true /@esbuild/darwin-x64@0.19.11: @@ -2446,6 +2475,7 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true + dev: true optional: true /@esbuild/freebsd-arm64@0.19.11: @@ -2462,6 +2492,7 @@ packages: cpu: [arm64] os: [freebsd] requiresBuild: true + dev: true optional: true /@esbuild/freebsd-x64@0.19.11: @@ -2478,6 +2509,7 @@ packages: cpu: [x64] os: [freebsd] requiresBuild: true + dev: true optional: true /@esbuild/linux-arm64@0.19.11: @@ -2494,6 +2526,7 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-arm@0.19.11: @@ -2510,6 +2543,7 @@ packages: cpu: [arm] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-ia32@0.19.11: @@ -2526,6 +2560,7 @@ packages: cpu: [ia32] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-loong64@0.19.11: @@ -2542,6 +2577,7 @@ packages: cpu: [loong64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-mips64el@0.19.11: @@ -2558,6 +2594,7 @@ packages: cpu: [mips64el] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-ppc64@0.19.11: @@ -2574,6 +2611,7 @@ packages: cpu: [ppc64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-riscv64@0.19.11: @@ -2590,6 +2628,7 @@ packages: cpu: [riscv64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-s390x@0.19.11: @@ -2606,6 +2645,7 @@ packages: cpu: [s390x] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-x64@0.19.11: @@ -2622,6 +2662,7 @@ packages: cpu: [x64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/netbsd-x64@0.19.11: @@ -2638,6 +2679,7 @@ packages: cpu: [x64] os: [netbsd] requiresBuild: true + dev: true optional: true /@esbuild/openbsd-x64@0.19.11: @@ -2654,6 +2696,7 @@ packages: cpu: [x64] os: [openbsd] requiresBuild: true + dev: true optional: true /@esbuild/sunos-x64@0.19.11: @@ -2670,6 +2713,7 @@ packages: cpu: [x64] os: [sunos] requiresBuild: true + dev: true optional: true /@esbuild/win32-arm64@0.19.11: @@ -2686,6 +2730,7 @@ packages: cpu: [arm64] os: [win32] requiresBuild: true + dev: true optional: true /@esbuild/win32-ia32@0.19.11: @@ -2702,6 +2747,7 @@ packages: cpu: [ia32] os: [win32] requiresBuild: true + dev: true optional: true /@esbuild/win32-x64@0.19.11: @@ -2718,6 +2764,7 @@ packages: cpu: [x64] os: [win32] requiresBuild: true + dev: true optional: true /@eslint-community/eslint-utils@4.4.0(eslint@8.56.0): @@ -5346,8 +5393,8 @@ packages: tsconfck: 3.0.0(typescript@5.3.3) unist-util-visit: 5.0.0 vfile: 6.0.1 - vite: 5.0.10(@types/node@20.10.6) - vitefu: 0.2.5(vite@5.0.10) + vite: 5.0.11(@types/node@20.10.6) + vitefu: 0.2.5(vite@5.0.11) which-pm: 2.1.1 yargs-parser: 21.1.1 zod: 3.22.4 @@ -6828,6 +6875,7 @@ packages: '@esbuild/win32-arm64': 0.19.5 '@esbuild/win32-ia32': 0.19.5 '@esbuild/win32-x64': 0.19.5 + dev: true /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} @@ -13565,7 +13613,7 @@ packages: - supports-color dev: true - /vite@5.0.10(@types/node@20.10.6): + /vite@5.0.10: resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -13593,12 +13641,12 @@ packages: terser: optional: true dependencies: - '@types/node': 20.10.6 esbuild: 0.19.5 postcss: 8.4.32 rollup: 4.9.2 optionalDependencies: fsevents: 2.3.3 + dev: true /vite@5.0.11(@types/node@20.10.6): resolution: {integrity: sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==} @@ -13629,13 +13677,13 @@ packages: optional: true dependencies: '@types/node': 20.10.6 - esbuild: 0.19.5 + esbuild: 0.19.11 postcss: 8.4.32 rollup: 4.9.2 optionalDependencies: fsevents: 2.3.3 - /vitefu@0.2.5(vite@5.0.10): + /vitefu@0.2.5(vite@5.0.11): resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} peerDependencies: vite: ^3.0.0 || ^4.0.0 || ^5.0.0 @@ -13643,7 +13691,7 @@ packages: vite: optional: true dependencies: - vite: 5.0.10(@types/node@20.10.6) + vite: 5.0.11(@types/node@20.10.6) /vitest@1.1.0(@vitest/ui@1.1.0): resolution: {integrity: sha512-oDFiCrw7dd3Jf06HoMtSRARivvyjHJaTxikFxuqJjO76U436PqlVw1uLn7a8OSPrhSfMGVaRakKpA2lePdw79A==} diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 56fc062c..075f4207 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1933,6 +1933,96 @@ exports[`runs examples > example "detune" example index 0 1`] = ` ] `; +exports[`runs examples > example "distort" example index 0 1`] = ` +[ + "[ 0/1 → 1/8 | s:hh distort:0 ]", + "[ 0/1 → 1/4 | s:bd distort:0 ]", + "[ 1/8 → 1/4 | s:hh distort:0 ]", + "[ 1/4 → 3/8 | s:hh distort:0 ]", + "[ 1/4 → 1/2 | s:sd distort:0 ]", + "[ 3/8 → 1/2 | s:hh distort:0 ]", + "[ 1/2 → 5/8 | s:hh distort:0 ]", + "[ 5/8 → 3/4 | s:bd distort:0 ]", + "[ 5/8 → 3/4 | s:hh distort:0 ]", + "[ 3/4 → 7/8 | s:hh distort:0 ]", + "[ 3/4 → 1/1 | s:sd distort:0 ]", + "[ 7/8 → 1/1 | s:hh distort:0 ]", + "[ 1/1 → 9/8 | s:hh distort:2 ]", + "[ 1/1 → 5/4 | s:bd distort:2 ]", + "[ 9/8 → 5/4 | s:hh distort:2 ]", + "[ 5/4 → 11/8 | s:hh distort:2 ]", + "[ 5/4 → 3/2 | s:sd distort:2 ]", + "[ 11/8 → 3/2 | s:hh distort:2 ]", + "[ 3/2 → 13/8 | s:hh distort:2 ]", + "[ 13/8 → 7/4 | s:bd distort:2 ]", + "[ 13/8 → 7/4 | s:hh distort:2 ]", + "[ 7/4 → 15/8 | s:hh distort:2 ]", + "[ 7/4 → 2/1 | s:sd distort:2 ]", + "[ 15/8 → 2/1 | s:hh distort:2 ]", + "[ 2/1 → 17/8 | s:hh distort:3 ]", + "[ 2/1 → 9/4 | s:bd distort:3 ]", + "[ 17/8 → 9/4 | s:hh distort:3 ]", + "[ 9/4 → 19/8 | s:hh distort:3 ]", + "[ 9/4 → 5/2 | s:sd distort:3 ]", + "[ 19/8 → 5/2 | s:hh distort:3 ]", + "[ 5/2 → 21/8 | s:hh distort:3 ]", + "[ 21/8 → 11/4 | s:bd distort:3 ]", + "[ 21/8 → 11/4 | s:hh distort:3 ]", + "[ 11/4 → 23/8 | s:hh distort:3 ]", + "[ 11/4 → 3/1 | s:sd distort:3 ]", + "[ 23/8 → 3/1 | s:hh distort:3 ]", + "[ 3/1 → 25/8 | s:hh distort:10 distortvol:0.5 ]", + "[ 3/1 → 13/4 | s:bd distort:10 distortvol:0.5 ]", + "[ 25/8 → 13/4 | s:hh distort:10 distortvol:0.5 ]", + "[ 13/4 → 27/8 | s:hh distort:10 distortvol:0.5 ]", + "[ 13/4 → 7/2 | s:sd distort:10 distortvol:0.5 ]", + "[ 27/8 → 7/2 | s:hh distort:10 distortvol:0.5 ]", + "[ 7/2 → 29/8 | s:hh distort:10 distortvol:0.5 ]", + "[ 29/8 → 15/4 | s:bd distort:10 distortvol:0.5 ]", + "[ 29/8 → 15/4 | s:hh distort:10 distortvol:0.5 ]", + "[ 15/4 → 31/8 | s:hh distort:10 distortvol:0.5 ]", + "[ 15/4 → 4/1 | s:sd distort:10 distortvol:0.5 ]", + "[ 31/8 → 4/1 | s:hh distort:10 distortvol:0.5 ]", +] +`; + +exports[`runs examples > example "distort" example index 1 1`] = ` +[ + "[ 0/1 → 1/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 1/8 → 1/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 1/4 → 3/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 3/8 → 1/2 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 1/2 → 5/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 5/8 → 3/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 3/4 → 7/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 7/8 → 1/1 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 1/1 → 9/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 9/8 → 5/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 5/4 → 11/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 11/8 → 3/2 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 3/2 → 13/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 13/8 → 7/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 7/4 → 15/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 15/8 → 2/1 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 2/1 → 17/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 17/8 → 9/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 9/4 → 19/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 19/8 → 5/2 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 5/2 → 21/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 21/8 → 11/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 11/4 → 23/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 23/8 → 3/1 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 3/1 → 25/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 25/8 → 13/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 13/4 → 27/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 27/8 → 7/2 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 7/2 → 29/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 29/8 → 15/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 15/4 → 31/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", + "[ 31/8 → 4/1 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:8 distortvol:0.4 ]", +] +`; + exports[`runs examples > example "djf" example index 0 1`] = ` [ "[ 0/1 → 1/4 | n:0 s:superzow octave:3 djf:0.5 ]", diff --git a/website/package.json b/website/package.json index af100889..7af9e61e 100644 --- a/website/package.json +++ b/website/package.json @@ -26,6 +26,7 @@ "@nanostores/react": "^0.7.1", "@strudel/codemirror": "workspace:*", "@strudel/core": "workspace:*", + "@strudel/draw": "workspace:*", "@strudel/csound": "workspace:*", "@strudel/desktopbridge": "workspace:*", "@strudel/hydra": "workspace:*", diff --git a/website/src/docs/Colors.jsx b/website/src/docs/Colors.jsx index 7cc7eab6..34cc2e49 100644 --- a/website/src/docs/Colors.jsx +++ b/website/src/docs/Colors.jsx @@ -1,5 +1,4 @@ -import { colorMap } from '@strudel/core/color.mjs'; -import React from 'react'; +import { colorMap } from '@strudel/draw'; const Colors = () => { return ( diff --git a/website/src/docs/MiniRepl.jsx b/website/src/docs/MiniRepl.jsx index 3afb609e..cb0f26f7 100644 --- a/website/src/docs/MiniRepl.jsx +++ b/website/src/docs/MiniRepl.jsx @@ -1,6 +1,7 @@ import { useState, useRef, useCallback, useMemo, useEffect } from 'react'; import { Icon } from './Icon'; -import { silence, getPunchcardPainter, noteToMidi, _mod } from '@strudel/core'; +import { silence, noteToMidi, _mod } from '@strudel/core'; +import { getPunchcardPainter } from '@strudel/draw'; import { transpiler } from '@strudel/transpiler'; import { getAudioContext, webaudioOutput, initAudioOnFirstClick } from '@strudel/webaudio'; import { StrudelMirror } from '@strudel/codemirror'; diff --git a/website/src/pages/img/example-[name].png.js b/website/src/pages/img/example-[name].png.js index 86d8a552..a38a0616 100644 --- a/website/src/pages/img/example-[name].png.js +++ b/website/src/pages/img/example-[name].png.js @@ -1,5 +1,5 @@ import { createCanvas } from 'canvas'; -import { pianoroll } from '@strudel/core'; +import { pianoroll } from '@strudel/draw'; import { evaluate } from '@strudel/transpiler'; import '../../../../test/runtime.mjs'; import * as tunes from '../../repl/tunes.mjs'; diff --git a/website/src/pages/learn/effects.mdx b/website/src/pages/learn/effects.mdx index 308ace71..03ef6ef3 100644 --- a/website/src/pages/learn/effects.mdx +++ b/website/src/pages/learn/effects.mdx @@ -238,9 +238,9 @@ Let's break down all pitch envelope controls: -## shape +## distort - + # Global Effects diff --git a/website/src/pages/swatch/[name].png.js b/website/src/pages/swatch/[name].png.js index c0a52a6b..ac150cfe 100644 --- a/website/src/pages/swatch/[name].png.js +++ b/website/src/pages/swatch/[name].png.js @@ -1,5 +1,5 @@ import { createCanvas } from 'canvas'; -import { pianoroll } from '@strudel/core'; +import { pianoroll } from '@strudel/draw'; import { evaluate } from '@strudel/transpiler'; import '../../../../test/runtime.mjs'; import { getMyPatterns } from '../../my_patterns'; diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index c5a8fa09..fb73b5cc 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -4,7 +4,8 @@ Copyright (C) 2022 Strudel contributors - see . */ -import { code2hash, getDrawContext, logger, silence } from '@strudel/core'; +import { code2hash, logger, silence } from '@strudel/core'; +import { getDrawContext } from '@strudel/draw'; import cx from '@src/cx.mjs'; import { transpiler } from '@strudel/transpiler'; import { @@ -17,6 +18,7 @@ import { import { defaultAudioDeviceName } from '../settings.mjs'; import { getAudioDevices, setAudioDevice } from './util.mjs'; import { StrudelMirror, defaultSettings } from '@strudel/codemirror'; +import { clearHydra } from '@strudel/hydra'; import { useCallback, useEffect, useRef, useState } from 'react'; import { settingsMap, useSettings } from '../settings.mjs'; import { @@ -75,6 +77,11 @@ export function Repl({ embedded = false }) { onUpdateState: (state) => { setReplState({ ...state }); }, + onToggle: (playing) => { + if (!playing) { + clearHydra(); + } + }, afterEval: (all) => { const { code } = all; setLatestCode(code); @@ -165,6 +172,7 @@ export function Repl({ embedded = false }) { const resetEditor = async () => { resetGlobalEffects(); clearCanvas(); + clearHydra(); resetLoadedSounds(); editorRef.current.repl.setCps(0.5); await prebake(); // declare default samples @@ -189,6 +197,7 @@ export function Repl({ embedded = false }) { setActivePattern(patternData.id); setViewingPatternData(patternData); clearCanvas(); + clearHydra(); resetLoadedSounds(); resetGlobalEffects(); await prebake(); // declare default samples diff --git a/website/src/repl/drawings.mjs b/website/src/repl/drawings.mjs index 30fef649..5508ebe8 100644 --- a/website/src/repl/drawings.mjs +++ b/website/src/repl/drawings.mjs @@ -24,7 +24,9 @@ angle(saw) `; // https://strudel.cc/?C31_NrcMfZEO -export const spiralflower = `const {innerWidth:ww,innerHeight:wh} = window; +export const spiralflower = `let {innerWidth:ww,innerHeight:wh} = window; +ww*=window.devicePixelRatio; +wh*=window.devicePixelRatio; const ctx = getDrawContext() const piDiv180 = Math.PI / 180; function fromPolar(angle, radius, cx, cy) { diff --git a/website/src/repl/util.mjs b/website/src/repl/util.mjs index 397e2801..6dba7dab 100644 --- a/website/src/repl/util.mjs +++ b/website/src/repl/util.mjs @@ -72,6 +72,7 @@ export async function getRandomTune() { export function loadModules() { let modules = [ import('@strudel/core'), + import('@strudel/draw'), import('@strudel/tonal'), import('@strudel/mini'), import('@strudel/xen'),