From 1416a0de5386099f23af796fc5b159150b73603b Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Tue, 27 Feb 2024 23:38:37 -0500 Subject: [PATCH 01/24] working --- packages/superdough/superdough.mjs | 3 +- packages/superdough/worklets.mjs | 110 ++++++++++++----------------- 2 files changed, 47 insertions(+), 66 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index f5674f2c..2eb42720 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -452,7 +452,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 })); + const shapeInput = Array.isArray(shape) ? { shape: shape[0], postgain: shape[1] } : { shape }; + shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', shapeInput)); compressorThreshold !== undefined && chain.push( diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 7bb43f87..43e58e33 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -1,7 +1,23 @@ -// 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 +const processSample = (inputs, outputs, processBlock) => { + const input = inputs[0]; + const output = outputs[0]; + const blockSize = 128; + if (input == null || output == null) { + return false; + } + for (let n = 0; n < blockSize; n++) { + input.forEach((inChannel, i) => { + const outChannel = output[i % output.length]; + const block = inChannel[n]; + outChannel[n] = processBlock(block, n, inChannel, outChannel); + }); + } + return true; +}; + +// 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 class CoarseProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { return [{ name: 'coarse', defaultValue: 1 }]; @@ -9,25 +25,15 @@ 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]; - } - } - } - return this.notStarted || hasInput; + let coarse = parameters.coarse[0] ?? 0; + coarse = Math.min(128, Math.max(1, Math.round(coarse * 128))); + return processSample(inputs, outputs, (block, n, inChannel, outChannel) => { + const value = n % coarse === 0 ? block : outChannel[n - 1]; + return value; + }); } } @@ -40,68 +46,42 @@ 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; - } - } - } - } - return this.notStarted || hasInput; + const bitMax = 16; + const bitMin = 1; + let crush = parameters.crush[0] ?? 8; + crush = Math.max(bitMin, bitMax - crush * bitMax); + + return processSample(inputs, outputs, (block) => { + const x = Math.pow(2, crush - 1); + return Math.round(block * x) / x; + }); } } 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; - } - } - } - return this.notStarted || hasInput; + let shape_param = parameters.shape[0]; + const postgain = Math.max(0.001, Math.min(1, parameters.postgain[0])); + const shape = shape_param * 100; + return processSample(inputs, outputs, (block, n) => { + const val = ((1 + shape) * block) / (1 + shape * Math.abs(block)); + return val * postgain; + }); } } From 360b9209355463b0c5a024617d2c7cffeae96312 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Tue, 27 Feb 2024 23:42:33 -0500 Subject: [PATCH 02/24] cleaning_up --- packages/superdough/worklets.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 43e58e33..7017be95 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -78,7 +78,7 @@ class ShapeProcessor extends AudioWorkletProcessor { let shape_param = parameters.shape[0]; const postgain = Math.max(0.001, Math.min(1, parameters.postgain[0])); const shape = shape_param * 100; - return processSample(inputs, outputs, (block, n) => { + return processSample(inputs, outputs, (block) => { const val = ((1 + shape) * block) / (1 + shape * Math.abs(block)); return val * postgain; }); From 972bfa3319e178731d5377cfda44da64d82fad61 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Wed, 28 Feb 2024 00:04:12 -0500 Subject: [PATCH 03/24] cleaning up --- packages/core/controls.mjs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 3b2b5aab..e11937ea 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -355,6 +355,7 @@ const generic_params = [ * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>") * */ + ['crush'], // TODO: currently duplicated with "native" legato // TODO: superdirt legato will do more: https://youtu.be/dQPmE1WaD1k?t=419 /** @@ -369,14 +370,16 @@ const generic_params = [ */ // ['legato'], // ['clhatdecay'], - ['crush'], + /** * fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers * * @name coarse - * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. + * @param {number | Pattern} factor number between 0 and 1 * @example - * s("bd sd [~ bd] sd,hh*8").coarse("<1 4 8 16 32>") + * s("bd sd [~ bd] sd,hh*8").coarse("<0 .1 .2 .8>") + * @example + * s("bd(5, 8),hh*8").coarse(saw.fast(3)), * */ ['coarse'], @@ -1215,11 +1218,15 @@ const generic_params = [ // ['scrash'], /** * Wave shaping distortion. CAUTION: it might get loud + * second option in optional array syntax (ex: ".9:.5") applies a postgain to the output + * most useful shape values are usually between 0 and 1 (depending on source gain), however there is no limit * * @name shape * @param {number | Pattern} distortion between 0 and 1 * @example - * s("bd sd [~ bd] sd,hh*8").shape("<0 .2 .4 .6 .8>") + * s("bd sd [~ bd] sd,hh*8").shape("<0 .1 .5 10:.5>") + * @example + * note("d1!8").s("sine").penv(36).pdecay(.12).decay(.2).shape("100:.4") * */ ['shape'], From a227c694088e3be77b04837205371215e10bd001 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Wed, 28 Feb 2024 10:24:26 -0500 Subject: [PATCH 04/24] adjusting params --- packages/core/controls.mjs | 17 +++++++---------- packages/superdough/worklets.mjs | 10 ++++------ 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index e11937ea..c034f1b5 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -355,7 +355,6 @@ const generic_params = [ * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>") * */ - ['crush'], // TODO: currently duplicated with "native" legato // TODO: superdirt legato will do more: https://youtu.be/dQPmE1WaD1k?t=419 /** @@ -370,16 +369,14 @@ const generic_params = [ */ // ['legato'], // ['clhatdecay'], - + ['crush'], /** - * fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers + * fake-resampling for lowering the sample rate * * @name coarse - * @param {number | Pattern} factor number between 0 and 1 + * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. * @example - * s("bd sd [~ bd] sd,hh*8").coarse("<0 .1 .2 .8>") - * @example - * s("bd(5, 8),hh*8").coarse(saw.fast(3)), + * s("bd sd [~ bd] sd,hh*8").coarse("<1 4 8 16 32>") * */ ['coarse'], @@ -1219,14 +1216,14 @@ const generic_params = [ /** * Wave shaping distortion. CAUTION: it might get loud * second option in optional array syntax (ex: ".9:.5") applies a postgain to the output - * most useful shape values are usually between 0 and 1 (depending on source gain), however there is no limit + * most useful shape values are usually between 0 and 10 (depending on source gain) * * @name shape * @param {number | Pattern} distortion between 0 and 1 * @example - * s("bd sd [~ bd] sd,hh*8").shape("<0 .1 .5 10:.5>") + * s("bd sd [~ bd] sd,hh*8").shape("<0 2 3 10:.5>") * @example - * note("d1!8").s("sine").penv(36).pdecay(.12).decay(.2).shape("100:.4") + * note("d1!8").s("sine").penv(36).pdecay(.12).decay(.23).shape("8:.4") * */ ['shape'], diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 7017be95..86167d0e 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -29,7 +29,7 @@ class CoarseProcessor extends AudioWorkletProcessor { process(inputs, outputs, parameters) { let coarse = parameters.coarse[0] ?? 0; - coarse = Math.min(128, Math.max(1, Math.round(coarse * 128))); + coarse = Math.max(1, coarse); return processSample(inputs, outputs, (block, n, inChannel, outChannel) => { const value = n % coarse === 0 ? block : outChannel[n - 1]; return value; @@ -49,10 +49,8 @@ class CrushProcessor extends AudioWorkletProcessor { } process(inputs, outputs, parameters) { - const bitMax = 16; - const bitMin = 1; let crush = parameters.crush[0] ?? 8; - crush = Math.max(bitMin, bitMax - crush * bitMax); + crush = Math.max(1, crush); return processSample(inputs, outputs, (block) => { const x = Math.pow(2, crush - 1); @@ -75,9 +73,9 @@ class ShapeProcessor extends AudioWorkletProcessor { } process(inputs, outputs, parameters) { - let shape_param = parameters.shape[0]; + let shape = parameters.shape[0]; const postgain = Math.max(0.001, Math.min(1, parameters.postgain[0])); - const shape = shape_param * 100; + shape = Math.expm1(shape); return processSample(inputs, outputs, (block) => { const val = ((1 + shape) * block) / (1 + shape * Math.abs(block)); return val * postgain; From c77b0098a7ec4290b6a4c8f53a338fdf3b4a82d5 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Wed, 28 Feb 2024 13:17:36 -0500 Subject: [PATCH 05/24] adjust param range --- packages/superdough/worklets.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 86167d0e..25885ce6 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -75,7 +75,7 @@ class ShapeProcessor extends AudioWorkletProcessor { process(inputs, outputs, parameters) { let shape = parameters.shape[0]; const postgain = Math.max(0.001, Math.min(1, parameters.postgain[0])); - shape = Math.expm1(shape); + shape = Math.expm1(shape * 5); return processSample(inputs, outputs, (block) => { const val = ((1 + shape) * block) / (1 + shape * Math.abs(block)); return val * postgain; From 1221c6144ca314fb3e80766f89b8bb93cb1a6b0d Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Wed, 28 Feb 2024 14:31:07 -0500 Subject: [PATCH 06/24] added distortion effect --- packages/core/controls.mjs | 22 +++++++++++++++++----- packages/superdough/superdough.mjs | 11 +++++++++-- packages/superdough/worklets.mjs | 28 +++++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index c034f1b5..42f4648a 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1214,19 +1214,31 @@ const generic_params = [ // ['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 - * most useful shape values are usually between 0 and 10 (depending on source gain) * * @name shape * @param {number | Pattern} distortion between 0 and 1 * @example - * s("bd sd [~ bd] sd,hh*8").shape("<0 2 3 10:.5>") - * @example - * note("d1!8").s("sine").penv(36).pdecay(.12).decay(.23).shape("8:.4") + * s("bd sd [~ bd] sd,hh*8").shape("<0 .2 .3 .95:.5>") * */ ['shape'], + + /** + * 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 + * @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") + * + */ + ['distort', '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) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 2eb42720..795354ad 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -311,6 +311,7 @@ export const superdough = async (value, deadline, hapDuration) => { coarse, crush, shape, + distort, pan, vowel, delay = 0, @@ -452,8 +453,14 @@ 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 })); - const shapeInput = Array.isArray(shape) ? { shape: shape[0], postgain: shape[1] } : { shape }; - shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', shapeInput)); + if (shape !== undefined) { + const input = Array.isArray(shape) ? { shape: shape[0], postgain: shape[1] } : { shape }; + chain.push(getWorklet(ac, 'shape-processor', input)); + } + if (distort !== undefined) { + const input = Array.isArray(distort) ? { distort: distort[0], postgain: distort[1] } : { distort }; + chain.push(getWorklet(ac, 'distort-processor', input)); + } compressorThreshold !== undefined && chain.push( diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 25885ce6..3f6d7ac9 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -75,7 +75,8 @@ class ShapeProcessor extends AudioWorkletProcessor { process(inputs, outputs, parameters) { let shape = parameters.shape[0]; const postgain = Math.max(0.001, Math.min(1, parameters.postgain[0])); - shape = Math.expm1(shape * 5); + shape = shape < 1 ? shape : 1.0 - 4e-10; + shape = (2.0 * shape) / (1.0 - shape); return processSample(inputs, outputs, (block) => { const val = ((1 + shape) * block) / (1 + shape * Math.abs(block)); return val * postgain; @@ -84,3 +85,28 @@ class ShapeProcessor extends AudioWorkletProcessor { } 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) { + let shape = parameters.distort[0]; + const postgain = Math.max(0.001, Math.min(1, parameters.postgain[0])); + shape = Math.expm1(shape); + return processSample(inputs, outputs, (block) => { + const val = ((1 + shape) * block) / (1 + shape * Math.abs(block)); + return val * postgain; + }); + } +} + +registerProcessor('distort-processor', DistortProcessor); From a3898c75891ef66239b887d34b18d3018130b0fe Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Wed, 28 Feb 2024 14:36:50 -0500 Subject: [PATCH 07/24] cleanup --- packages/core/controls.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 42f4648a..e3c8cc1c 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1364,7 +1364,6 @@ const generic_params = [ ['ring'], ['ringf'], ['ringdf'], - ['distort'], ['freeze'], ['xsdelay'], ['tsdelay'], From e3463b3c3d35bd8524410155a6b729963a0316d2 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Wed, 28 Feb 2024 17:08:58 -0500 Subject: [PATCH 08/24] merged new controls file --- packages/core/controls.mjs | 2884 ++++++++++++++++++------------------ 1 file changed, 1449 insertions(+), 1435 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index e3c8cc1c..42fa8cba 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -5,1428 +5,8 @@ This program is free software: you can redistribute it and/or modify it under th */ import { Pattern, register, sequence } from './pattern.mjs'; -import { zipWith } from './util.mjs'; -const controls = {}; -const generic_params = [ - /** - * Select a sound / sample by name. When using mininotation, you can also optionally supply 'n' and 'gain' parameters - * separated by ':'. - * - * @name s - * @param {string | Pattern} sound The sound / pattern of sounds to pick - * @synonyms sound - * @example - * s("bd hh") - * @example - * s("bd:0 bd:1 bd:0:0.3 bd:1:1.4") - * - */ - [['s', 'n', 'gain'], 'sound'], - /** - * Define a custom webaudio node to use as a sound source. - * - * @name source - * @param {function} getSource - * @synonyms src - * - */ - ['source', 'src'], - /** - * Selects the given index from the sample map. - * Numbers too high will wrap around. - * `n` can also be used to play midi numbers, but it is recommended to use `note` instead. - * - * @name n - * @param {number | Pattern} value sample index starting from 0 - * @example - * s("bd sd [~ bd] sd,hh*6").n("<0 1>") - */ - // also see https://github.com/tidalcycles/strudel/pull/63 - ['n'], - /** - * Plays the given note name or midi number. A note name consists of - * - * - a letter (a-g or A-G) - * - optional accidentals (b or #) - * - optional octave number (0-9). Defaults to 3 - * - * Examples of valid note names: `c`, `bb`, `Bb`, `f#`, `c3`, `A4`, `Eb2`, `c#5` - * - * You can also use midi numbers instead of note names, where 69 is mapped to A4 440Hz in 12EDO. - * - * @name note - * @example - * note("c a f e") - * @example - * note("c4 a4 f4 e4") - * @example - * note("60 69 65 64") - */ - [['note', 'n']], - - /** - * A pattern of numbers that speed up (or slow down) samples while they play. Currently only supported by osc / superdirt. - * - * @name accelerate - * @param {number | Pattern} amount acceleration. - * @superdirtOnly - * @example - * s("sax").accelerate("<0 1 2 4 8 16>").slow(2).osc() - * - */ - ['accelerate'], - /** - * Controls the gain by an exponential amount. - * - * @name gain - * @param {number | Pattern} amount gain. - * @example - * s("hh*8").gain(".4!2 1 .4!2 1 .4 1").fast(2) - * - */ - ['gain'], - /** - * Gain applied after all effects have been processed. - * - * @name postgain - * @example - * s("bd sd [~ bd] sd,hh*8") - * .compressor("-20:20:10:.002:.02").postgain(1.5) - * - */ - ['postgain'], - /** - * Like `gain`, but linear. - * - * @name amp - * @param {number | Pattern} amount gain. - * @superdirtOnly - * @example - * s("bd*8").amp(".1*2 .5 .1*2 .5 .1 .5").osc() - * - */ - ['amp'], - /** - * Amplitude envelope attack time: Specifies how long it takes for the sound to reach its peak value, relative to the onset. - * - * @name attack - * @param {number | Pattern} attack time in seconds. - * @synonyms att - * @example - * note("c3 e3 f3 g3").attack("<0 .1 .5>") - * - */ - ['attack', 'att'], - - /** - * Sets the Frequency Modulation Harmonicity Ratio. - * Controls the timbre of the sound. - * Whole numbers and simple ratios sound more natural, - * while decimal numbers and complex ratios sound metallic. - * - * @name fmh - * @param {number | Pattern} harmonicity - * @example - * note("c e g b g e") - * .fm(4) - * .fmh("<1 2 1.5 1.61>") - * .scope() - * - */ - [['fmh', 'fmi'], 'fmh'], - /** - * Sets the Frequency Modulation of the synth. - * Controls the modulation index, which defines the brightness of the sound. - * - * @name fm - * @param {number | Pattern} brightness modulation index - * @synonyms fmi - * @example - * note("c e g b g e") - * .fm("<0 1 2 8 32>") - * .scope() - * - */ - [['fmi', 'fmh'], 'fm'], - // fm envelope - /** - * Ramp type of fm envelope. Exp might be a bit broken.. - * - * @name fmenv - * @param {number | Pattern} type lin | exp - * @example - * note("c e g b g e") - * .fm(4) - * .fmdecay(.2) - * .fmsustain(0) - * .fmenv("") - * .scope() - * - */ - ['fmenv'], - /** - * Attack time for the FM envelope: time it takes to reach maximum modulation - * - * @name fmattack - * @param {number | Pattern} time attack time - * @example - * note("c e g b g e") - * .fm(4) - * .fmattack("<0 .05 .1 .2>") - * .scope() - * - */ - ['fmattack'], - /** - * Decay time for the FM envelope: seconds until the sustain level is reached after the attack phase. - * - * @name fmdecay - * @param {number | Pattern} time decay time - * @example - * note("c e g b g e") - * .fm(4) - * .fmdecay("<.01 .05 .1 .2>") - * .fmsustain(.4) - * .scope() - * - */ - ['fmdecay'], - /** - * Sustain level for the FM envelope: how much modulation is applied after the decay phase - * - * @name fmsustain - * @param {number | Pattern} level sustain level - * @example - * note("c e g b g e") - * .fm(4) - * .fmdecay(.1) - * .fmsustain("<1 .75 .5 0>") - * .scope() - * - */ - ['fmsustain'], - // these are not really useful... skipping for now - ['fmrelease'], - ['fmvelocity'], - - /** - * Select the sound bank to use. To be used together with `s`. The bank name (+ "_") will be prepended to the value of `s`. - * - * @name bank - * @param {string | Pattern} bank the name of the bank - * @example - * s("bd sd [~ bd] sd").bank('RolandTR909') // = s("RolandTR909_bd RolandTR909_sd") - * - */ - ['bank'], - - ['analyze'], // analyser node send amount 0 - 1 (used by scope) - ['fft'], // fftSize of analyser - - /** - * Amplitude envelope decay time: the time it takes after the attack time to reach the sustain level. - * Note that the decay is only audible if the sustain value is lower than 1. - * - * @name decay - * @param {number | Pattern} time decay time in seconds - * @example - * note("c3 e3 f3 g3").decay("<.1 .2 .3 .4>").sustain(0) - * - */ - ['decay', 'dec'], - /** - * Amplitude envelope sustain level: The level which is reached after attack / decay, being sustained until the offset. - * - * @name sustain - * @param {number | Pattern} gain sustain level between 0 and 1 - * @synonyms sus - * @example - * note("c3 e3 f3 g3").decay(.2).sustain("<0 .1 .4 .6 1>") - * - */ - ['sustain', 'sus'], - /** - * Amplitude envelope release time: The time it takes after the offset to go from sustain level to zero. - * - * @name release - * @param {number | Pattern} time release time in seconds - * @synonyms rel - * @example - * note("c3 e3 g3 c4").release("<0 .1 .4 .6 1>/2") - * - */ - ['release', 'rel'], - ['hold'], - // TODO: in tidal, it seems to be normalized - /** - * Sets the center frequency of the **b**and-**p**ass **f**ilter. When using mininotation, you - * can also optionally supply the 'bpq' parameter separated by ':'. - * - * @name bpf - * @param {number | Pattern} frequency center frequency - * @synonyms bandf, bp - * @example - * s("bd sd [~ bd] sd,hh*6").bpf("<1000 2000 4000 8000>") - * - */ - [['bandf', 'bandq', 'bpenv'], 'bpf', 'bp'], - // TODO: in tidal, it seems to be normalized - /** - * Sets the **b**and-**p**ass **q**-factor (resonance). - * - * @name bpq - * @param {number | Pattern} q q factor - * @synonyms bandq - * @example - * s("bd sd [~ bd] sd").bpf(500).bpq("<0 1 2 3>") - * - */ - // currently an alias of 'bandq' https://github.com/tidalcycles/strudel/issues/496 - // ['bpq'], - ['bandq', 'bpq'], - /** - * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. - * - * @memberof Pattern - * @name begin - * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample - * @example - * samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/Dirt-Samples/master/') - * s("rave").begin("<0 .25 .5 .75>").fast(2) - * - */ - ['begin'], - /** - * The same as .begin, but cuts off the end off each sample. - * - * @memberof Pattern - * @name end - * @param {number | Pattern} length 1 = whole sample, .5 = half sample, .25 = quarter sample etc.. - * @example - * s("bd*2,oh*4").end("<.1 .2 .5 1>").fast(2) - * - */ - ['end'], - /** - * Loops the sample. - * Note that the tempo of the loop is not synced with the cycle tempo. - * To change the loop region, use loopBegin / loopEnd. - * - * @name loop - * @param {number | Pattern} on If 1, the sample is looped - * @example - * s("casio").loop(1) - * - */ - ['loop'], - /** - * Begin to loop at a specific point in the sample (inbetween `begin` and `end`). - * Note that the loop point must be inbetween `begin` and `end`, and before `loopEnd`! - * Note: Samples starting with wt_ will automatically loop! (wt = wavetable) - * - * @name loopBegin - * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample - * @synonyms loopb - * @example - * s("space").loop(1) - * .loopBegin("<0 .125 .25>").scope() - */ - ['loopBegin', 'loopb'], - /** - * - * End the looping section at a specific point in the sample (inbetween `begin` and `end`). - * Note that the loop point must be inbetween `begin` and `end`, and after `loopBegin`! - * - * @name loopEnd - * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample - * @synonyms loope - * @example - * s("space").loop(1) - * .loopEnd("<1 .75 .5 .25>").scope() - */ - ['loopEnd', 'loope'], - /** - * bit crusher effect. - * - * @name crush - * @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction). - * @example - * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>") - * - */ - // TODO: currently duplicated with "native" legato - // TODO: superdirt legato will do more: https://youtu.be/dQPmE1WaD1k?t=419 - /** - * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. - * - * @name legato - * @param {number | Pattern} duration between 0 and 1, where 1 is the length of the whole hap time - * @noAutocomplete - * @example - * "c4 eb4 g4 bb4".legato("<0.125 .25 .5 .75 1 2 4>") - * - */ - // ['legato'], - // ['clhatdecay'], - ['crush'], - /** - * fake-resampling for lowering the sample rate - * - * @name coarse - * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. - * @example - * s("bd sd [~ bd] sd,hh*8").coarse("<1 4 8 16 32>") - * - */ - ['coarse'], - - /** - * Allows you to set the output channels on the interface - * - * @name channels - * @synonyms ch - * - * @param {number | Pattern} channels pattern the output channels - * @example - * note("e a d b g").channels("3:4") - * - */ - ['channels', 'ch'], - - ['phaserrate', 'phasr'], // superdirt only - - /** - * Phaser audio effect that approximates popular guitar pedals. - * - * @name phaser - * @synonyms ph - * @param {number | Pattern} speed speed of modulation - * @example - * n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5) - * .phaser("<1 2 4 8>") - * - */ - [['phaser', 'phaserdepth', 'phasercenter', 'phasersweep'], 'ph'], - - /** - * The frequency sweep range of the lfo for the phaser effect. Defaults to 2000 - * - * @name phasersweep - * @synonyms phs - * @param {number | Pattern} phasersweep most useful values are between 0 and 4000 - * @example - * n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5) - * .phaser(2).phasersweep("<800 2000 4000>") - * - */ - ['phasersweep', 'phs'], - - /** - * The center frequency of the phaser in HZ. Defaults to 1000 - * - * @name phasercenter - * @synonyms phc - * @param {number | Pattern} centerfrequency in HZ - * @example - * n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5) - * .phaser(2).phasercenter("<800 2000 4000>") - * - */ - - ['phasercenter', 'phc'], - - /** - * The amount the signal is affected by the phaser effect. Defaults to 0.75 - * - * @name phaserdepth - * @synonyms phd - * @param {number | Pattern} depth number between 0 and 1 - * @example - * n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5) - * .phaser(2).phaserdepth("<0 .5 .75 1>") - * - */ - ['phaserdepth', 'phd', 'phasdp'], // also a superdirt control - - /** - * choose the channel the pattern is sent to in superdirt - * - * @name channel - * @param {number | Pattern} channel channel number - * - */ - ['channel'], - /** - * In the style of classic drum-machines, `cut` will stop a playing sample as soon as another samples with in same cutgroup is to be played. An example would be an open hi-hat followed by a closed one, essentially muting the open. - * - * @name cut - * @param {number | Pattern} group cut group number - * @example - * s("[oh hh]*4").cut(1) - * - */ - ['cut'], - /** - * Applies the cutoff frequency of the **l**ow-**p**ass **f**ilter. - * - * When using mininotation, you can also optionally add the 'lpq' parameter, separated by ':'. - * - * @name lpf - * @param {number | Pattern} frequency audible between 0 and 20000 - * @synonyms cutoff, ctf, lp - * @example - * s("bd sd [~ bd] sd,hh*6").lpf("<4000 2000 1000 500 200 100>") - * @example - * s("bd*16").lpf("1000:0 1000:10 1000:20 1000:30") - * - */ - [['cutoff', 'resonance', 'lpenv'], 'ctf', 'lpf', 'lp'], - - /** - * Sets the lowpass filter envelope modulation depth. - * @name lpenv - * @param {number | Pattern} modulation depth of the lowpass filter envelope between 0 and _n_ - * @synonyms lpe - * @example - * note("c2 e2 f2 g2") - * .sound('sawtooth') - * .lpf(500) - * .lpa(.5) - * .lpenv("<4 2 1 0 -1 -2 -4>/4") - */ - ['lpenv', 'lpe'], - /** - * Sets the highpass filter envelope modulation depth. - * @name hpenv - * @param {number | Pattern} modulation depth of the highpass filter envelope between 0 and _n_ - * @synonyms hpe - * @example - * note("c2 e2 f2 g2") - * .sound('sawtooth') - * .hpf(500) - * .hpa(.5) - * .hpenv("<4 2 1 0 -1 -2 -4>/4") - */ - ['hpenv', 'hpe'], - /** - * Sets the bandpass filter envelope modulation depth. - * @name bpenv - * @param {number | Pattern} modulation depth of the bandpass filter envelope between 0 and _n_ - * @synonyms bpe - * @example - * note("c2 e2 f2 g2") - * .sound('sawtooth') - * .bpf(500) - * .bpa(.5) - * .bpenv("<4 2 1 0 -1 -2 -4>/4") - */ - ['bpenv', 'bpe'], - /** - * Sets the attack duration for the lowpass filter envelope. - * @name lpattack - * @param {number | Pattern} attack time of the filter envelope - * @synonyms lpa - * @example - * note("c2 e2 f2 g2") - * .sound('sawtooth') - * .lpf(500) - * .lpa("<.5 .25 .1 .01>/4") - * .lpenv(4) - */ - ['lpattack', 'lpa'], - /** - * Sets the attack duration for the highpass filter envelope. - * @name hpattack - * @param {number | Pattern} attack time of the highpass filter envelope - * @synonyms hpa - * @example - * note("c2 e2 f2 g2") - * .sound('sawtooth') - * .hpf(500) - * .hpa("<.5 .25 .1 .01>/4") - * .hpenv(4) - */ - ['hpattack', 'hpa'], - /** - * Sets the attack duration for the bandpass filter envelope. - * @name bpattack - * @param {number | Pattern} attack time of the bandpass filter envelope - * @synonyms bpa - * @example - * note("c2 e2 f2 g2") - * .sound('sawtooth') - * .bpf(500) - * .bpa("<.5 .25 .1 .01>/4") - * .bpenv(4) - */ - ['bpattack', 'bpa'], - /** - * Sets the decay duration for the lowpass filter envelope. - * @name lpdecay - * @param {number | Pattern} decay time of the filter envelope - * @synonyms lpd - * @example - * note("c2 e2 f2 g2") - * .sound('sawtooth') - * .lpf(500) - * .lpd("<.5 .25 .1 0>/4") - * .lps(0.2) - * .lpenv(4) - */ - ['lpdecay', 'lpd'], - /** - * Sets the decay duration for the highpass filter envelope. - * @name hpdecay - * @param {number | Pattern} decay time of the highpass filter envelope - * @synonyms hpd - * @example - * note("c2 e2 f2 g2") - * .sound('sawtooth') - * .hpf(500) - * .hpd("<.5 .25 .1 0>/4") - * .hps(0.2) - * .hpenv(4) - */ - ['hpdecay', 'hpd'], - /** - * Sets the decay duration for the bandpass filter envelope. - * @name bpdecay - * @param {number | Pattern} decay time of the bandpass filter envelope - * @synonyms bpd - * @example - * note("c2 e2 f2 g2") - * .sound('sawtooth') - * .bpf(500) - * .bpd("<.5 .25 .1 0>/4") - * .bps(0.2) - * .bpenv(4) - */ - ['bpdecay', 'bpd'], - /** - * Sets the sustain amplitude for the lowpass filter envelope. - * @name lpsustain - * @param {number | Pattern} sustain amplitude of the lowpass filter envelope - * @synonyms lps - * @example - * note("c2 e2 f2 g2") - * .sound('sawtooth') - * .lpf(500) - * .lpd(.5) - * .lps("<0 .25 .5 1>/4") - * .lpenv(4) - */ - ['lpsustain', 'lps'], - /** - * Sets the sustain amplitude for the highpass filter envelope. - * @name hpsustain - * @param {number | Pattern} sustain amplitude of the highpass filter envelope - * @synonyms hps - * @example - * note("c2 e2 f2 g2") - * .sound('sawtooth') - * .hpf(500) - * .hpd(.5) - * .hps("<0 .25 .5 1>/4") - * .hpenv(4) - */ - ['hpsustain', 'hps'], - /** - * Sets the sustain amplitude for the bandpass filter envelope. - * @name bpsustain - * @param {number | Pattern} sustain amplitude of the bandpass filter envelope - * @synonyms bps - * @example - * note("c2 e2 f2 g2") - * .sound('sawtooth') - * .bpf(500) - * .bpd(.5) - * .bps("<0 .25 .5 1>/4") - * .bpenv(4) - */ - ['bpsustain', 'bps'], - /** - * Sets the release time for the lowpass filter envelope. - * @name lprelease - * @param {number | Pattern} release time of the filter envelope - * @synonyms lpr - * @example - * note("c2 e2 f2 g2") - * .sound('sawtooth') - * .clip(.5) - * .lpf(500) - * .lpenv(4) - * .lpr("<.5 .25 .1 0>/4") - * .release(.5) - */ - ['lprelease', 'lpr'], - /** - * Sets the release time for the highpass filter envelope. - * @name hprelease - * @param {number | Pattern} release time of the highpass filter envelope - * @synonyms hpr - * @example - * note("c2 e2 f2 g2") - * .sound('sawtooth') - * .clip(.5) - * .hpf(500) - * .hpenv(4) - * .hpr("<.5 .25 .1 0>/4") - * .release(.5) - */ - ['hprelease', 'hpr'], - /** - * Sets the release time for the bandpass filter envelope. - * @name bprelease - * @param {number | Pattern} release time of the bandpass filter envelope - * @synonyms bpr - * @example - * note("c2 e2 f2 g2") - * .sound('sawtooth') - * .clip(.5) - * .bpf(500) - * .bpenv(4) - * .bpr("<.5 .25 .1 0>/4") - * .release(.5) - */ - ['bprelease', 'bpr'], - /** - * Sets the filter type. The 24db filter is more aggressive. More types might be added in the future. - * @name ftype - * @param {number | Pattern} type 12db (default) or 24db - * @example - * note("c2 e2 f2 g2") - * .sound('sawtooth') - * .lpf(500) - * .bpenv(4) - * .ftype("12db 24db") - */ - ['ftype'], - ['fanchor'], - /** - * Applies the cutoff frequency of the **h**igh-**p**ass **f**ilter. - * - * When using mininotation, you can also optionally add the 'hpq' parameter, separated by ':'. - * - * @name hpf - * @param {number | Pattern} frequency audible between 0 and 20000 - * @synonyms hp, hcutoff - * @example - * s("bd sd [~ bd] sd,hh*8").hpf("<4000 2000 1000 500 200 100>") - * @example - * s("bd sd [~ bd] sd,hh*8").hpf("<2000 2000:25>") - * - */ - // currently an alias of 'hcutoff' https://github.com/tidalcycles/strudel/issues/496 - // ['hpf'], - /** - * Applies a vibrato to the frequency of the oscillator. - * - * @name vib - * @synonyms vibrato, v - * @param {number | Pattern} frequency of the vibrato in hertz - * @example - * note("a e") - * .vib("<.5 1 2 4 8 16>") - * @example - * // change the modulation depth with ":" - * note("a e") - * .vib("<.5 1 2 4 8 16>:12") - */ - [['vib', 'vibmod'], 'vibrato', 'v'], - /** - * Adds pink noise to the mix - * - * @name noise - * @param {number | Pattern} wet wet amount - * @example - * sound("/2") - */ - ['noise'], - /** - * Sets the vibrato depth in semitones. Only has an effect if `vibrato` | `vib` | `v` is is also set - * - * @name vibmod - * @synonyms vmod - * @param {number | Pattern} depth of vibrato (in semitones) - * @example - * note("a e").vib(4) - * .vibmod("<.25 .5 1 2 12>") - * @example - * // change the vibrato frequency with ":" - * note("a e") - * .vibmod("<.25 .5 1 2 12>:8") - */ - [['vibmod', 'vib'], 'vmod'], - [['hcutoff', 'hresonance', 'hpenv'], 'hpf', 'hp'], - /** - * Controls the **h**igh-**p**ass **q**-value. - * - * @name hpq - * @param {number | Pattern} q resonance factor between 0 and 50 - * @synonyms hresonance - * @example - * s("bd sd [~ bd] sd,hh*8").hpf(2000).hpq("<0 10 20 30>") - * - */ - ['hresonance', 'hpq'], - /** - * Controls the **l**ow-**p**ass **q**-value. - * - * @name lpq - * @param {number | Pattern} q resonance factor between 0 and 50 - * @synonyms resonance - * @example - * s("bd sd [~ bd] sd,hh*8").lpf(2000).lpq("<0 10 20 30>") - * - */ - // currently an alias of 'resonance' https://github.com/tidalcycles/strudel/issues/496 - ['resonance', 'lpq'], - /** - * 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 - * @example - * n("0 3 7 [10,24]").s('superzow').octave(3).djf("<.5 .25 .5 .75>").osc() - * - */ - ['djf'], - // ['cutoffegint'], - // TODO: does not seem to work - /** - * Sets the level of the delay signal. - * - * When using mininotation, you can also optionally add the 'delaytime' and 'delayfeedback' parameter, - * separated by ':'. - * - * - * @name delay - * @param {number | Pattern} level between 0 and 1 - * @example - * s("bd bd").delay("<0 .25 .5 1>") - * @example - * s("bd bd").delay("0.65:0.25:0.9 0.65:0.125:0.7") - * - */ - [['delay', 'delaytime', 'delayfeedback']], - /** - * Sets the level of the signal that is fed back into the delay. - * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it - * - * @name delayfeedback - * @param {number | Pattern} feedback between 0 and 1 - * @synonyms delayfb, dfb - * @example - * s("bd").delay(.25).delayfeedback("<.25 .5 .75 1>") - * - */ - ['delayfeedback', 'delayfb', 'dfb'], - /** - * Sets the time of the delay effect. - * - * @name delaytime - * @param {number | Pattern} seconds between 0 and Infinity - * @synonyms delayt, dt - * @example - * s("bd bd").delay(.25).delaytime("<.125 .25 .5 1>") - * - */ - ['delaytime', 'delayt', 'dt'], - /* // TODO: test - * Specifies whether delaytime is calculated relative to cps. - * - * @name lock - * @param {number | Pattern} enable When set to 1, delaytime is a direct multiple of a cycle. - * @example - * s("sd").delay().lock(1).osc() - * - */ - ['lock'], - /** - * Set detune of oscillators. Works only with some synths, see tidal doc - * - * @name detune - * @param {number | Pattern} amount between 0 and 1 - * @synonyms det - * @superdirtOnly - * @example - * n("0 3 7").s('superzow').octave(3).detune("<0 .25 .5 1 2>").osc() - * - */ - ['detune', 'det'], - /** - * Set dryness of reverb. See `room` and `size` for more information about reverb. - * - * @name dry - * @param {number | Pattern} dry 0 = wet, 1 = dry - * @example - * n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc() - * @superdirtOnly - * - */ - ['dry'], - // TODO: does not seem to do anything - /* - * Used when using `begin`/`end` or `chop`/`striate` and friends, to change the fade out time of the 'grain' envelope. - * - * @name fadeTime - * @param {number | Pattern} time between 0 and 1 - * @example - * s("oh*4").end(.1).fadeTime("<0 .2 .4 .8>").osc() - * - */ - ['fadeTime', 'fadeOutTime'], - // TODO: see above - ['fadeInTime'], - /** - * Set frequency of sound. - * - * @name freq - * @param {number | Pattern} frequency in Hz. the audible range is between 20 and 20000 Hz - * @example - * freq("220 110 440 110").s("superzow").osc() - * @example - * freq("110".mul.out(".5 1.5 .6 [2 3]")).s("superzow").osc() - * - */ - ['freq'], - // pitch envelope - /** - * Attack time of pitch envelope. - * - * @name pattack - * @synonyms patt - * @param {number | Pattern} time time in seconds - * @example - * note("c eb g bb").pattack("0 .1 .25 .5").slow(2) - * - */ - ['pattack', 'patt'], - /** - * Decay time of pitch envelope. - * - * @name pdecay - * @synonyms pdec - * @param {number | Pattern} time time in seconds - * @example - * note("").pdecay("<0 .1 .25 .5>") - * - */ - ['pdecay', 'pdec'], - // TODO: how to use psustain?! - ['psustain', 'psus'], - /** - * Release time of pitch envelope - * - * @name prelease - * @synonyms prel - * @param {number | Pattern} time time in seconds - * @example - * note(" ~") - * .release(.5) // to hear the pitch release - * .prelease("<0 .1 .25 .5>") - * - */ - ['prelease', 'prel'], - /** - * Amount of pitch envelope. Negative values will flip the envelope. - * If you don't set other pitch envelope controls, `pattack:.2` will be the default. - * - * @name penv - * @param {number | Pattern} semitones change in semitones - * @example - * note("c") - * .penv("<12 7 1 .5 0 -1 -7 -12>") - * - */ - ['penv'], - /** - * Curve of envelope. Defaults to linear. exponential is good for kicks - * - * @name pcurve - * @param {number | Pattern} type 0 = linear, 1 = exponential - * @example - * note("g1*4") - * .s("sine").pdec(.5) - * .penv(32) - * .pcurve("<0 1>") - * - */ - ['pcurve'], - /** - * Sets the range anchor of the envelope: - * - anchor 0: range = [note, note + penv] - * - anchor 1: range = [note - penv, note] - * If you don't set an anchor, the value will default to the psustain value. - * - * @name panchor - * @param {number | Pattern} anchor anchor offset - * @example - * note("c c4").penv(12).panchor("<0 .5 1 .5>") - * - */ - ['panchor'], - // TODO: https://tidalcycles.org/docs/configuration/MIDIOSC/control-voltage/#gate - ['gate', 'gat'], - // ['hatgrain'], - // ['lagogo'], - // ['lclap'], - // ['lclaves'], - // ['lclhat'], - // ['lcrash'], - // TODO: - // https://tidalcycles.org/docs/reference/audio_effects/#leslie-1 - // https://tidalcycles.org/docs/reference/audio_effects/#leslie - /** - * Emulation of a Leslie speaker: speakers rotating in a wooden amplified cabinet. - * - * @name leslie - * @param {number | Pattern} wet between 0 and 1 - * @example - * n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc() - * @superdirtOnly - * - */ - ['leslie'], - /** - * Rate of modulation / rotation for leslie effect - * - * @name lrate - * @param {number | Pattern} rate 6.7 for fast, 0.7 for slow - * @example - * n("0,4,7").s("supersquare").leslie(1).lrate("<1 2 4 8>").osc() - * @superdirtOnly - * - */ - // TODO: the rate seems to "lag" (in the example, 1 will be fast) - ['lrate'], - /** - * Physical size of the cabinet in meters. Be careful, it might be slightly larger than your computer. Affects the Doppler amount (pitch warble) - * - * @name lsize - * @param {number | Pattern} meters somewhere between 0 and 1 - * @example - * n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc() - * @superdirtOnly - * - */ - ['lsize'], - /** - * Sets the displayed text for an event on the pianoroll - * - * @name label - * @param {string} label text to display - */ - ['activeLabel'], - [['label', 'activeLabel']], - // ['lfo'], - // ['lfocutoffint'], - // ['lfodelay'], - // ['lfoint'], - // ['lfopitchint'], - // ['lfoshape'], - // ['lfosync'], - // ['lhitom'], - // ['lkick'], - // ['llotom'], - // ['lophat'], - // ['lsnare'], - ['degree'], // TODO: what is this? not found in tidal doc - ['mtranspose'], // TODO: what is this? not found in tidal doc - ['ctranspose'], // TODO: what is this? not found in tidal doc - ['harmonic'], // TODO: what is this? not found in tidal doc - ['stepsPerOctave'], // TODO: what is this? not found in tidal doc - ['octaveR'], // TODO: what is this? not found in tidal doc - // TODO: why is this needed? what's the difference to late / early? Answer: it's in seconds, and delays the message at - // OSC time (so can't be negative, at least not beyond the latency value) - ['nudge'], - // TODO: the following doc is just a guess, it's not documented in tidal doc. - /** - * Sets the default octave of a synth. - * - * @name octave - * @param {number | Pattern} octave octave number - * @example - * n("0,4,7").s('supersquare').octave("<3 4 5 6>").osc() - * @superDirtOnly - */ - ['octave'], - - // ['ophatdecay'], - // TODO: example - /** - * An `orbit` is a global parameter context for patterns. Patterns with the same orbit will share the same global effects. - * - * @name orbit - * @param {number | Pattern} number - * @example - * stack( - * s("hh*6").delay(.5).delaytime(.25).orbit(1), - * s("~ sd ~ sd").delay(.5).delaytime(.125).orbit(2) - * ) - */ - ['orbit'], - ['overgain'], // TODO: what is this? not found in tidal doc Answer: gain is limited to maximum of 2. This allows you to go over that - ['overshape'], // TODO: what is this? not found in tidal doc. Similar to above, but limited to 1 - /** - * Sets position in stereo. - * - * @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>") - * @example - * s("bd rim sd rim bd ~ cp rim").pan(sine.slow(2)) - * - */ - ['pan'], - // TODO: this has no effect (see example) - /* - * Controls how much multichannel output is fanned out - * - * @name panspan - * @param {number | Pattern} span between -inf and inf, negative is backwards ordering - * @example - * s("[bd hh]*2").pan("<.5 1 .5 0>").panspan("<0 .5 1>").osc() - * - */ - ['panspan'], - // TODO: this has no effect (see example) - /* - * Controls how much multichannel output is spread - * - * @name pansplay - * @param {number | Pattern} spread between 0 and 1 - * @example - * s("[bd hh]*2").pan("<.5 1 .5 0>").pansplay("<0 .5 1>").osc() - * - */ - ['pansplay'], - ['panwidth'], - ['panorient'], - // ['pitch1'], - // ['pitch2'], - // ['pitch3'], - // ['portamento'], - // TODO: LFO rate see https://tidalcycles.org/docs/patternlib/tutorials/synthesizers/#supersquare - ['rate'], - // TODO: slide param for certain synths - ['slide'], - // TODO: detune? https://tidalcycles.org/docs/patternlib/tutorials/synthesizers/#supersquare - ['semitone'], - // TODO: dedup with synth param, see https://tidalcycles.org/docs/reference/synthesizers/#superpiano - // ['velocity'], - ['voice'], // TODO: synth param - - // voicings // https://github.com/tidalcycles/strudel/issues/506 - ['chord'], // chord to voice, like C Eb Fm7 G7. the symbols can be defined via addVoicings - ['dictionary', 'dict'], // which dictionary to use for the voicings - ['anchor'], // the top note to align the voicing to, defaults to c5 - ['offset'], // how the voicing is offset from the anchored position - ['octaves'], // how many octaves are voicing steps spread apart, defaults to 1 - [['mode', 'anchor']], // below = anchor note will be removed from the voicing, useful for melody harmonization - - /** - * Sets the level of reverb. - * - * When using mininotation, you can also optionally add the 'size' parameter, separated by ':'. - * - * @name room - * @param {number | Pattern} level between 0 and 1 - * @example - * s("bd sd [~ bd] sd").room("<0 .2 .4 .6 .8 1>") - * @example - * s("bd sd [~ bd] sd").room("<0.9:1 0.9:4>") - * - */ - [['room', 'size']], - /** - * Reverb lowpass starting frequency (in hertz). - * When this property is changed, the reverb will be recaculated, so only change this sparsely.. - * - * @name roomlp - * @synonyms rlp - * @param {number} frequency between 0 and 20000hz - * @example - * s("bd sd [~ bd] sd").room(0.5).rlp(10000) - * @example - * s("bd sd [~ bd] sd").room(0.5).rlp(5000) - */ - ['roomlp', 'rlp'], - /** - * Reverb lowpass frequency at -60dB (in hertz). - * When this property is changed, the reverb will be recaculated, so only change this sparsely.. - * - * @name roomdim - * @synonyms rdim - * @param {number} frequency between 0 and 20000hz - * @example - * s("bd sd [~ bd] sd").room(0.5).rlp(10000).rdim(8000) - * @example - * s("bd sd [~ bd] sd").room(0.5).rlp(5000).rdim(400) - * - */ - ['roomdim', 'rdim'], - /** - * Reverb fade time (in seconds). - * When this property is changed, the reverb will be recaculated, so only change this sparsely.. - * - * @name roomfade - * @synonyms rfade - * @param {number} seconds for the reverb to fade - * @example - * s("bd sd [~ bd] sd").room(0.5).rlp(10000).rfade(0.5) - * @example - * s("bd sd [~ bd] sd").room(0.5).rlp(5000).rfade(4) - * - */ - ['roomfade', 'rfade'], - /** - * Sets the sample to use as an impulse response for the reverb. - * @name iresponse - * @param {string | Pattern} sample to use as an impulse response - * @synonyms ir - * @example - * s("bd sd [~ bd] sd").room(.8).ir("") - * - */ - [['ir', 'i'], 'iresponse'], - /** - * Sets the room size of the reverb, see `room`. - * When this property is changed, the reverb will be recaculated, so only change this sparsely.. - * - * @name roomsize - * @param {number | Pattern} size between 0 and 10 - * @synonyms rsize, sz, size - * @example - * s("bd sd [~ bd] sd").room(.8).rsize(1) - * @example - * s("bd sd [~ bd] sd").room(.8).rsize(4) - * - */ - // TODO: find out why : - // s("bd sd [~ bd] sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc() - // .. does not work. Is it because room is only one effect? - ['roomsize', 'size', 'sz', 'rsize'], - // ['sagogo'], - // ['sclap'], - // ['sclaves'], - // ['scrash'], - /** - * (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 - * @example - * s("bd sd [~ bd] sd,hh*8").shape("<0 .2 .3 .95:.5>") - * - */ - ['shape'], - - /** - * 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 - * @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") - * - */ - ['distort', '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) - * - * @name compressor - * @example - * s("bd sd [~ bd] sd,hh*8") - * .compressor("-20:20:10:.002:.02") - * - */ - [['compressor', 'compressorRatio', 'compressorKnee', 'compressorAttack', 'compressorRelease']], - ['compressorKnee'], - ['compressorRatio'], - ['compressorAttack'], - ['compressorRelease'], - /** - * Changes the speed of sample playback, i.e. a cheap way of changing pitch. - * - * @name speed - * @param {number | Pattern} speed -inf to inf, negative numbers play the sample backwards. - * @example - * s("bd*6").speed("1 2 4 1 -2 -4") - * @example - * speed("1 1.5*2 [2 1.1]").s("piano").clip(1) - * - */ - ['speed'], - /** - * Used in conjunction with `speed`, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`. - * - * @name unit - * @param {number | string | Pattern} unit see description above - * @example - * speed("1 2 .5 3").s("bd").unit("c").osc() - * @superdirtOnly - * - */ - ['unit'], - /** - * Made by Calum Gunn. Reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter. The SuperCollider manual defines Squiz as: - * - * "A simplistic pitch-raising algorithm. It's not meant to sound natural; its sound is reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter, depending on the input. The algorithm works by cutting the signal into fragments (delimited by upwards-going zero-crossings) and squeezing those fragments in the time domain (i.e. simply playing them back faster than they came in), leaving silences inbetween. All the parameters apart from memlen can be modulated." - * - * @name squiz - * @param {number | Pattern} squiz Try passing multiples of 2 to it - 2, 4, 8 etc. - * @example - * squiz("2 4/2 6 [8 16]").s("bd").osc() - * @superdirtOnly - * - */ - ['squiz'], - // ['stutterdepth'], // TODO: what is this? not found in tidal doc - // ['stuttertime'], // TODO: what is this? not found in tidal doc - // ['timescale'], // TODO: what is this? not found in tidal doc - // ['timescalewin'], // TODO: what is this? not found in tidal doc - // ['tomdecay'], - // ['vcfegint'], - // ['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 ae aa oe ue y uh un en an on, corresponding to [a] [e] [i] [o] [u] [æ] [ɑ] [ø] [y] [ɯ] [ʌ] [œ̃] [ɛ̃] [ɑ̃] [ɔ̃]. Aliases: aa = å = ɑ, oe = ø = ö, y = ı, ae = æ. - * @example - * note("[c2 >]*2").s('sawtooth') - * .vowel(">") - * @example - * s("bd sd mt ht bd [~ cp] ht lt").vowel("[a|e|i|o|u]") - * - */ - ['vowel'], - /* // TODO: find out how it works - * Made by Calum Gunn. Divides an audio stream into tiny segments, using the signal's zero-crossings as segment boundaries, and discards a fraction of them. Takes a number between 1 and 100, denoted the percentage of segments to drop. The SuperCollider manual describes the Waveloss effect this way: - * - * Divide an audio stream into tiny segments, using the signal's zero-crossings as segment boundaries, and discard a fraction of them (i.e. replace them with silence of the same length). The technique was described by Trevor Wishart in a lecture. Parameters: the filter drops drop out of out of chunks. mode can be 1 to drop chunks in a simple deterministic fashion (e.g. always dropping the first 30 out of a set of 40 segments), or 2 to drop chunks randomly but in an appropriate proportion.) - * - * mode: ? - * waveloss: ? - * - * @name waveloss - */ - ['waveloss'], - /* - * Noise crackle density - * - * @name density - * @param {number | Pattern} density between 0 and x - * @example - * s("crackle*4").density("<0.01 0.04 0.2 0.5>".slow(4)) - * - */ - ['density'], - // TODO: midi effects? - ['dur'], - // ['modwheel'], - ['expression'], - ['sustainpedal'], - /* // TODO: doesn't seem to do anything - * - * Tremolo Audio DSP effect - * - * @name tremolodepth - * @param {number | Pattern} depth between 0 and 1 - * @example - * n("0,4,7").tremolodepth("<0 .3 .6 .9>").osc() - * - */ - ['tremolodepth', 'tremdp'], - ['tremolorate', 'tremr'], - - ['fshift'], - ['fshiftnote'], - ['fshiftphase'], - - ['triode'], - ['krush'], - ['kcutoff'], - ['octer'], - ['octersub'], - ['octersubsub'], - ['ring'], - ['ringf'], - ['ringdf'], - ['freeze'], - ['xsdelay'], - ['tsdelay'], - ['real'], - ['imag'], - ['enhance'], - ['partials'], - ['comb'], - ['smear'], - ['scram'], - ['binshift'], - ['hbrick'], - ['lbrick'], - ['midichan'], - ['control'], - ['ccn'], - ['ccv'], - ['polyTouch'], - ['midibend'], - ['miditouch'], - ['ctlNum'], - ['frameRate'], - ['frames'], - ['hours'], - ['midicmd'], - ['minutes'], - ['progNum'], - ['seconds'], - ['songPtr'], - ['uid'], - ['val'], - ['cps'], - /** - * Multiplies the duration with the given number. Also cuts samples off at the end if they exceed the duration. - * In tidal, this would be done with legato, [which has a complicated history in strudel](https://github.com/tidalcycles/strudel/issues/111). - * For now, if you're coming from tidal, just think clip = legato. - * - * @name clip - * @param {number | Pattern} factor >= 0 - * @example - * note("c a f e").s("piano").clip("<.5 1 2>") - * - */ - ['clip'], - - // ZZFX - ['zrand'], - ['curve'], - ['slide'], // superdirt duplicate - ['deltaSlide'], - ['pitchJump'], - ['pitchJumpTime'], - ['lfo', 'repeatTime'], - ['znoise'], // noise on the frequency or as bubo calls it "frequency fog" :) - ['zmod'], - ['zcrush'], // like crush but scaled differently - ['zdelay'], - ['tremolo'], - ['zzfx'], -]; -// TODO: slice / splice https://www.youtube.com/watch?v=hKhPdO0RKDQ&list=PL2lW1zNIIwj3bDkh-Y3LUGDuRcoUigoDs&index=13 - -controls.createParam = function (names) { +export function createParam(names) { const name = Array.isArray(names) ? names[0] : names; var withVal; @@ -1458,20 +38,1456 @@ controls.createParam = function (names) { }; Pattern.prototype[name] = setter; return func; -}; +} -generic_params.forEach(([names, ...aliases]) => { +function registerControl(names, ...aliases) { const name = Array.isArray(names) ? names[0] : names; - controls[name] = controls.createParam(names); - + let bag = {}; + bag[name] = createParam(names); aliases.forEach((alias) => { - controls[alias] = controls[name]; + bag[alias] = bag[name]; Pattern.prototype[alias] = Pattern.prototype[name]; }); -}); + return bag; +} -controls.createParams = (...names) => - names.reduce((acc, name) => Object.assign(acc, { [name]: controls.createParam(name) }), {}); +/** + * Select a sound / sample by name. When using mininotation, you can also optionally supply 'n' and 'gain' parameters + * separated by ':'. + * + * @name s + * @param {string | Pattern} sound The sound / pattern of sounds to pick + * @synonyms sound + * @example + * s("bd hh") + * @example + * s("bd:0 bd:1 bd:0:0.3 bd:1:1.4") + * + */ +export const { s, sound } = registerControl(['s', 'n', 'gain'], 'sound'); + +/** + * Define a custom webaudio node to use as a sound source. + * + * @name source + * @param {function} getSource + * @synonyms src + * + */ +export const { source, src } = registerControl('source', 'src'); +/** + * Selects the given index from the sample map. + * Numbers too high will wrap around. + * `n` can also be used to play midi numbers, but it is recommended to use `note` instead. + * + * @name n + * @param {number | Pattern} value sample index starting from 0 + * @example + * s("bd sd [~ bd] sd,hh*6").n("<0 1>") + */ +// also see https://github.com/tidalcycles/strudel/pull/63 +export const { n } = registerControl('n'); +/** + * Plays the given note name or midi number. A note name consists of + * + * - a letter (a-g or A-G) + * - optional accidentals (b or #) + * - optional octave number (0-9). Defaults to 3 + * + * Examples of valid note names: `c`, `bb`, `Bb`, `f#`, `c3`, `A4`, `Eb2`, `c#5` + * + * You can also use midi numbers instead of note names, where 69 is mapped to A4 440Hz in 12EDO. + * + * @name note + * @example + * note("c a f e") + * @example + * note("c4 a4 f4 e4") + * @example + * note("60 69 65 64") + */ +export const { note } = registerControl(['note', 'n']); + +/** + * A pattern of numbers that speed up (or slow down) samples while they play. Currently only supported by osc / superdirt. + * + * @name accelerate + * @param {number | Pattern} amount acceleration. + * @superdirtOnly + * @example + * s("sax").accelerate("<0 1 2 4 8 16>").slow(2).osc() + * + */ +export const { accelerate } = registerControl('accelerate'); +/** + * Controls the gain by an exponential amount. + * + * @name gain + * @param {number | Pattern} amount gain. + * @example + * s("hh*8").gain(".4!2 1 .4!2 1 .4 1").fast(2) + * + */ +export const { gain } = registerControl('gain'); +/** + * Gain applied after all effects have been processed. + * + * @name postgain + * @example + * s("bd sd [~ bd] sd,hh*8") + * .compressor("-20:20:10:.002:.02").postgain(1.5) + * + */ +export const { postgain } = registerControl('postgain'); +/** + * Like `gain`, but linear. + * + * @name amp + * @param {number | Pattern} amount gain. + * @superdirtOnly + * @example + * s("bd*8").amp(".1*2 .5 .1*2 .5 .1 .5").osc() + * + */ +export const { amp } = registerControl('amp'); +/** + * Amplitude envelope attack time: Specifies how long it takes for the sound to reach its peak value, relative to the onset. + * + * @name attack + * @param {number | Pattern} attack time in seconds. + * @synonyms att + * @example + * note("c3 e3 f3 g3").attack("<0 .1 .5>") + * + */ +export const { attack, att } = registerControl('attack', 'att'); + +/** + * Sets the Frequency Modulation Harmonicity Ratio. + * Controls the timbre of the sound. + * Whole numbers and simple ratios sound more natural, + * while decimal numbers and complex ratios sound metallic. + * + * @name fmh + * @param {number | Pattern} harmonicity + * @example + * note("c e g b g e") + * .fm(4) + * .fmh("<1 2 1.5 1.61>") + * .scope() + * + */ +export const { fmh } = registerControl(['fmh', 'fmi'], 'fmh'); +/** + * Sets the Frequency Modulation of the synth. + * Controls the modulation index, which defines the brightness of the sound. + * + * @name fm + * @param {number | Pattern} brightness modulation index + * @synonyms fmi + * @example + * note("c e g b g e") + * .fm("<0 1 2 8 32>") + * .scope() + * + */ +export const { fmi, fm } = registerControl(['fmi', 'fmh'], 'fm'); +// fm envelope +/** + * Ramp type of fm envelope. Exp might be a bit broken.. + * + * @name fmenv + * @param {number | Pattern} type lin | exp + * @example + * note("c e g b g e") + * .fm(4) + * .fmdecay(.2) + * .fmsustain(0) + * .fmenv("") + * .scope() + * + */ +export const { fmenv } = registerControl('fmenv'); +/** + * Attack time for the FM envelope: time it takes to reach maximum modulation + * + * @name fmattack + * @param {number | Pattern} time attack time + * @example + * note("c e g b g e") + * .fm(4) + * .fmattack("<0 .05 .1 .2>") + * .scope() + * + */ +export const { fmattack } = registerControl('fmattack'); +/** + * Decay time for the FM envelope: seconds until the sustain level is reached after the attack phase. + * + * @name fmdecay + * @param {number | Pattern} time decay time + * @example + * note("c e g b g e") + * .fm(4) + * .fmdecay("<.01 .05 .1 .2>") + * .fmsustain(.4) + * .scope() + * + */ +export const { fmdecay } = registerControl('fmdecay'); +/** + * Sustain level for the FM envelope: how much modulation is applied after the decay phase + * + * @name fmsustain + * @param {number | Pattern} level sustain level + * @example + * note("c e g b g e") + * .fm(4) + * .fmdecay(.1) + * .fmsustain("<1 .75 .5 0>") + * .scope() + * + */ +export const { fmsustain } = registerControl('fmsustain'); +// these are not really useful... skipping for now +export const { fmrelease } = registerControl('fmrelease'); +export const { fmvelocity } = registerControl('fmvelocity'); + +/** + * Select the sound bank to use. To be used together with `s`. The bank name (+ "_") will be prepended to the value of `s`. + * + * @name bank + * @param {string | Pattern} bank the name of the bank + * @example + * s("bd sd [~ bd] sd").bank('RolandTR909') // = s("RolandTR909_bd RolandTR909_sd") + * + */ +export const { bank } = registerControl('bank'); + +// analyser node send amount 0 - 1 (used by scope) +export const { analyze } = registerControl('analyze'); +// fftSize of analyser +export const { fft } = registerControl('fft'); + +/** + * Amplitude envelope decay time: the time it takes after the attack time to reach the sustain level. + * Note that the decay is only audible if the sustain value is lower than 1. + * + * @name decay + * @param {number | Pattern} time decay time in seconds + * @example + * note("c3 e3 f3 g3").decay("<.1 .2 .3 .4>").sustain(0) + * + */ +export const { decay, dec } = registerControl('decay', 'dec'); +/** + * Amplitude envelope sustain level: The level which is reached after attack / decay, being sustained until the offset. + * + * @name sustain + * @param {number | Pattern} gain sustain level between 0 and 1 + * @synonyms sus + * @example + * note("c3 e3 f3 g3").decay(.2).sustain("<0 .1 .4 .6 1>") + * + */ +export const { sustain, sus } = registerControl('sustain', 'sus'); +/** + * Amplitude envelope release time: The time it takes after the offset to go from sustain level to zero. + * + * @name release + * @param {number | Pattern} time release time in seconds + * @synonyms rel + * @example + * note("c3 e3 g3 c4").release("<0 .1 .4 .6 1>/2") + * + */ +export const { release, rel } = registerControl('release', 'rel'); +export const { hold } = registerControl('hold'); +// TODO: in tidal, it seems to be normalized +/** + * Sets the center frequency of the **b**and-**p**ass **f**ilter. When using mininotation, you + * can also optionally supply the 'bpq' parameter separated by ':'. + * + * @name bpf + * @param {number | Pattern} frequency center frequency + * @synonyms bandf, bp + * @example + * s("bd sd [~ bd] sd,hh*6").bpf("<1000 2000 4000 8000>") + * + */ +export const { bandf, bpf, bp } = registerControl(['bandf', 'bandq', 'bpenv'], 'bpf', 'bp'); +// TODO: in tidal, it seems to be normalized +/** + * Sets the **b**and-**p**ass **q**-factor (resonance). + * + * @name bpq + * @param {number | Pattern} q q factor + * @synonyms bandq + * @example + * s("bd sd [~ bd] sd").bpf(500).bpq("<0 1 2 3>") + * + */ +// currently an alias of 'bandq' https://github.com/tidalcycles/strudel/issues/496 +// ['bpq'], +export const { bandq, bpq } = registerControl('bandq', 'bpq'); +/** + * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. + * + * @memberof Pattern + * @name begin + * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample + * @example + * samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/dirt-samples') + * s("rave").begin("<0 .25 .5 .75>").fast(2) + * + */ +export const { begin } = registerControl('begin'); +/** + * The same as .begin, but cuts off the end off each sample. + * + * @memberof Pattern + * @name end + * @param {number | Pattern} length 1 = whole sample, .5 = half sample, .25 = quarter sample etc.. + * @example + * s("bd*2,oh*4").end("<.1 .2 .5 1>").fast(2) + * + */ +export const { end } = registerControl('end'); +/** + * Loops the sample. + * Note that the tempo of the loop is not synced with the cycle tempo. + * To change the loop region, use loopBegin / loopEnd. + * + * @name loop + * @param {number | Pattern} on If 1, the sample is looped + * @example + * s("casio").loop(1) + * + */ +export const { loop } = registerControl('loop'); +/** + * Begin to loop at a specific point in the sample (inbetween `begin` and `end`). + * Note that the loop point must be inbetween `begin` and `end`, and before `loopEnd`! + * Note: Samples starting with wt_ will automatically loop! (wt = wavetable) + * + * @name loopBegin + * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample + * @synonyms loopb + * @example + * s("space").loop(1) + * .loopBegin("<0 .125 .25>").scope() + */ +export const { loopBegin, loopb } = registerControl('loopBegin', 'loopb'); +/** + * + * End the looping section at a specific point in the sample (inbetween `begin` and `end`). + * Note that the loop point must be inbetween `begin` and `end`, and after `loopBegin`! + * + * @name loopEnd + * @param {number | Pattern} time between 0 and 1, where 1 is the length of the sample + * @synonyms loope + * @example + * s("space").loop(1) + * .loopEnd("<1 .75 .5 .25>").scope() + */ +export const { loopEnd, loope } = registerControl('loopEnd', 'loope'); +/** + * bit crusher effect. + * + * @name crush + * @param {number | Pattern} depth between 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction). + * @example + * s(",hh*3").fast(2).crush("<16 8 7 6 5 4 3 2>") + * + */ +// TODO: currently duplicated with "native" legato +// TODO: superdirt legato will do more: https://youtu.be/dQPmE1WaD1k?t=419 +/** + * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. + * + * @name legato + * @param {number | Pattern} duration between 0 and 1, where 1 is the length of the whole hap time + * @noAutocomplete + * @example + * "c4 eb4 g4 bb4".legato("<0.125 .25 .5 .75 1 2 4>") + * + */ +// ['legato'], +// ['clhatdecay'], +export const { crush } = registerControl('crush'); +/** + * fake-resampling for lowering the sample rate. Caution: This effect seems to only work in chromium based browsers + * + * @name coarse + * @param {number | Pattern} factor 1 for original 2 for half, 3 for a third and so on. + * @example + * s("bd sd [~ bd] sd,hh*8").coarse("<1 4 8 16 32>") + * + */ +export const { coarse } = registerControl('coarse'); + +/** + * Allows you to set the output channels on the interface + * + * @name channels + * @synonyms ch + * + * @param {number | Pattern} channels pattern the output channels + * @example + * note("e a d b g").channels("3:4") + * + */ +export const { channels, ch } = registerControl('channels', 'ch'); + +// superdirt only +export const { phaserrate, phasr } = registerControl('phaserrate', 'phasr'); + +/** + * Phaser audio effect that approximates popular guitar pedals. + * + * @name phaser + * @synonyms ph + * @param {number | Pattern} speed speed of modulation + * @example + * n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5) + * .phaser("<1 2 4 8>") + * + */ +export const { phaser, ph } = registerControl(['phaser', 'phaserdepth', 'phasercenter', 'phasersweep'], 'ph'); + +/** + * The frequency sweep range of the lfo for the phaser effect. Defaults to 2000 + * + * @name phasersweep + * @synonyms phs + * @param {number | Pattern} phasersweep most useful values are between 0 and 4000 + * @example + * n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5) + * .phaser(2).phasersweep("<800 2000 4000>") + * + */ +export const { phasersweep, phs } = registerControl('phasersweep', 'phs'); + +/** + * The center frequency of the phaser in HZ. Defaults to 1000 + * + * @name phasercenter + * @synonyms phc + * @param {number | Pattern} centerfrequency in HZ + * @example + * n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5) + * .phaser(2).phasercenter("<800 2000 4000>") + * + */ + +export const { phasercenter, phc } = registerControl('phasercenter', 'phc'); + +/** + * The amount the signal is affected by the phaser effect. Defaults to 0.75 + * + * @name phaserdepth + * @synonyms phd + * @param {number | Pattern} depth number between 0 and 1 + * @example + * n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5) + * .phaser(2).phaserdepth("<0 .5 .75 1>") + * + */ +// also a superdirt control +export const { phaserdepth, phd, phasdp } = registerControl('phaserdepth', 'phd', 'phasdp'); + +/** + * choose the channel the pattern is sent to in superdirt + * + * @name channel + * @param {number | Pattern} channel channel number + * + */ +export const { channel } = registerControl('channel'); +/** + * In the style of classic drum-machines, `cut` will stop a playing sample as soon as another samples with in same cutgroup is to be played. An example would be an open hi-hat followed by a closed one, essentially muting the open. + * + * @name cut + * @param {number | Pattern} group cut group number + * @example + * s("[oh hh]*4").cut(1) + * + */ +export const { cut } = registerControl('cut'); +/** + * Applies the cutoff frequency of the **l**ow-**p**ass **f**ilter. + * + * When using mininotation, you can also optionally add the 'lpq' parameter, separated by ':'. + * + * @name lpf + * @param {number | Pattern} frequency audible between 0 and 20000 + * @synonyms cutoff, ctf, lp + * @example + * s("bd sd [~ bd] sd,hh*6").lpf("<4000 2000 1000 500 200 100>") + * @example + * s("bd*16").lpf("1000:0 1000:10 1000:20 1000:30") + * + */ +export const { cutoff, ctf, lpf, lp } = registerControl(['cutoff', 'resonance', 'lpenv'], 'ctf', 'lpf', 'lp'); + +/** + * Sets the lowpass filter envelope modulation depth. + * @name lpenv + * @param {number | Pattern} modulation depth of the lowpass filter envelope between 0 and _n_ + * @synonyms lpe + * @example + * note("c2 e2 f2 g2") + * .sound('sawtooth') + * .lpf(500) + * .lpa(.5) + * .lpenv("<4 2 1 0 -1 -2 -4>/4") + */ +export const { lpenv, lpe } = registerControl('lpenv', 'lpe'); +/** + * Sets the highpass filter envelope modulation depth. + * @name hpenv + * @param {number | Pattern} modulation depth of the highpass filter envelope between 0 and _n_ + * @synonyms hpe + * @example + * note("c2 e2 f2 g2") + * .sound('sawtooth') + * .hpf(500) + * .hpa(.5) + * .hpenv("<4 2 1 0 -1 -2 -4>/4") + */ +export const { hpenv, hpe } = registerControl('hpenv', 'hpe'); +/** + * Sets the bandpass filter envelope modulation depth. + * @name bpenv + * @param {number | Pattern} modulation depth of the bandpass filter envelope between 0 and _n_ + * @synonyms bpe + * @example + * note("c2 e2 f2 g2") + * .sound('sawtooth') + * .bpf(500) + * .bpa(.5) + * .bpenv("<4 2 1 0 -1 -2 -4>/4") + */ +export const { bpenv, bpe } = registerControl('bpenv', 'bpe'); +/** + * Sets the attack duration for the lowpass filter envelope. + * @name lpattack + * @param {number | Pattern} attack time of the filter envelope + * @synonyms lpa + * @example + * note("c2 e2 f2 g2") + * .sound('sawtooth') + * .lpf(500) + * .lpa("<.5 .25 .1 .01>/4") + * .lpenv(4) + */ +export const { lpattack, lpa } = registerControl('lpattack', 'lpa'); +/** + * Sets the attack duration for the highpass filter envelope. + * @name hpattack + * @param {number | Pattern} attack time of the highpass filter envelope + * @synonyms hpa + * @example + * note("c2 e2 f2 g2") + * .sound('sawtooth') + * .hpf(500) + * .hpa("<.5 .25 .1 .01>/4") + * .hpenv(4) + */ +export const { hpattack, hpa } = registerControl('hpattack', 'hpa'); +/** + * Sets the attack duration for the bandpass filter envelope. + * @name bpattack + * @param {number | Pattern} attack time of the bandpass filter envelope + * @synonyms bpa + * @example + * note("c2 e2 f2 g2") + * .sound('sawtooth') + * .bpf(500) + * .bpa("<.5 .25 .1 .01>/4") + * .bpenv(4) + */ +export const { bpattack, bpa } = registerControl('bpattack', 'bpa'); +/** + * Sets the decay duration for the lowpass filter envelope. + * @name lpdecay + * @param {number | Pattern} decay time of the filter envelope + * @synonyms lpd + * @example + * note("c2 e2 f2 g2") + * .sound('sawtooth') + * .lpf(500) + * .lpd("<.5 .25 .1 0>/4") + * .lps(0.2) + * .lpenv(4) + */ +export const { lpdecay, lpd } = registerControl('lpdecay', 'lpd'); +/** + * Sets the decay duration for the highpass filter envelope. + * @name hpdecay + * @param {number | Pattern} decay time of the highpass filter envelope + * @synonyms hpd + * @example + * note("c2 e2 f2 g2") + * .sound('sawtooth') + * .hpf(500) + * .hpd("<.5 .25 .1 0>/4") + * .hps(0.2) + * .hpenv(4) + */ +export const { hpdecay, hpd } = registerControl('hpdecay', 'hpd'); +/** + * Sets the decay duration for the bandpass filter envelope. + * @name bpdecay + * @param {number | Pattern} decay time of the bandpass filter envelope + * @synonyms bpd + * @example + * note("c2 e2 f2 g2") + * .sound('sawtooth') + * .bpf(500) + * .bpd("<.5 .25 .1 0>/4") + * .bps(0.2) + * .bpenv(4) + */ +export const { bpdecay, bpd } = registerControl('bpdecay', 'bpd'); +/** + * Sets the sustain amplitude for the lowpass filter envelope. + * @name lpsustain + * @param {number | Pattern} sustain amplitude of the lowpass filter envelope + * @synonyms lps + * @example + * note("c2 e2 f2 g2") + * .sound('sawtooth') + * .lpf(500) + * .lpd(.5) + * .lps("<0 .25 .5 1>/4") + * .lpenv(4) + */ +export const { lpsustain, lps } = registerControl('lpsustain', 'lps'); +/** + * Sets the sustain amplitude for the highpass filter envelope. + * @name hpsustain + * @param {number | Pattern} sustain amplitude of the highpass filter envelope + * @synonyms hps + * @example + * note("c2 e2 f2 g2") + * .sound('sawtooth') + * .hpf(500) + * .hpd(.5) + * .hps("<0 .25 .5 1>/4") + * .hpenv(4) + */ +export const { hpsustain, hps } = registerControl('hpsustain', 'hps'); +/** + * Sets the sustain amplitude for the bandpass filter envelope. + * @name bpsustain + * @param {number | Pattern} sustain amplitude of the bandpass filter envelope + * @synonyms bps + * @example + * note("c2 e2 f2 g2") + * .sound('sawtooth') + * .bpf(500) + * .bpd(.5) + * .bps("<0 .25 .5 1>/4") + * .bpenv(4) + */ +export const { bpsustain, bps } = registerControl('bpsustain', 'bps'); +/** + * Sets the release time for the lowpass filter envelope. + * @name lprelease + * @param {number | Pattern} release time of the filter envelope + * @synonyms lpr + * @example + * note("c2 e2 f2 g2") + * .sound('sawtooth') + * .clip(.5) + * .lpf(500) + * .lpenv(4) + * .lpr("<.5 .25 .1 0>/4") + * .release(.5) + */ +export const { lprelease, lpr } = registerControl('lprelease', 'lpr'); +/** + * Sets the release time for the highpass filter envelope. + * @name hprelease + * @param {number | Pattern} release time of the highpass filter envelope + * @synonyms hpr + * @example + * note("c2 e2 f2 g2") + * .sound('sawtooth') + * .clip(.5) + * .hpf(500) + * .hpenv(4) + * .hpr("<.5 .25 .1 0>/4") + * .release(.5) + */ +export const { hprelease, hpr } = registerControl('hprelease', 'hpr'); +/** + * Sets the release time for the bandpass filter envelope. + * @name bprelease + * @param {number | Pattern} release time of the bandpass filter envelope + * @synonyms bpr + * @example + * note("c2 e2 f2 g2") + * .sound('sawtooth') + * .clip(.5) + * .bpf(500) + * .bpenv(4) + * .bpr("<.5 .25 .1 0>/4") + * .release(.5) + */ +export const { bprelease, bpr } = registerControl('bprelease', 'bpr'); +/** + * Sets the filter type. The 24db filter is more aggressive. More types might be added in the future. + * @name ftype + * @param {number | Pattern} type 12db (default) or 24db + * @example + * note("c2 e2 f2 g2") + * .sound('sawtooth') + * .lpf(500) + * .bpenv(4) + * .ftype("12db 24db") + */ +export const { ftype } = registerControl('ftype'); +export const { fanchor } = registerControl('fanchor'); +/** + * Applies the cutoff frequency of the **h**igh-**p**ass **f**ilter. + * + * When using mininotation, you can also optionally add the 'hpq' parameter, separated by ':'. + * + * @name hpf + * @param {number | Pattern} frequency audible between 0 and 20000 + * @synonyms hp, hcutoff + * @example + * s("bd sd [~ bd] sd,hh*8").hpf("<4000 2000 1000 500 200 100>") + * @example + * s("bd sd [~ bd] sd,hh*8").hpf("<2000 2000:25>") + * + */ +// currently an alias of 'hcutoff' https://github.com/tidalcycles/strudel/issues/496 +// ['hpf'], +/** + * Applies a vibrato to the frequency of the oscillator. + * + * @name vib + * @synonyms vibrato, v + * @param {number | Pattern} frequency of the vibrato in hertz + * @example + * note("a e") + * .vib("<.5 1 2 4 8 16>") + * @example + * // change the modulation depth with ":" + * note("a e") + * .vib("<.5 1 2 4 8 16>:12") + */ +export const { vib, vibrato, v } = registerControl(['vib', 'vibmod'], 'vibrato', 'v'); +/** + * Adds pink noise to the mix + * + * @name noise + * @param {number | Pattern} wet wet amount + * @example + * sound("/2") + */ +export const { noise } = registerControl('noise'); +/** + * Sets the vibrato depth in semitones. Only has an effect if `vibrato` | `vib` | `v` is is also set + * + * @name vibmod + * @synonyms vmod + * @param {number | Pattern} depth of vibrato (in semitones) + * @example + * note("a e").vib(4) + * .vibmod("<.25 .5 1 2 12>") + * @example + * // change the vibrato frequency with ":" + * note("a e") + * .vibmod("<.25 .5 1 2 12>:8") + */ +export const { vibmod, vmod } = registerControl(['vibmod', 'vib'], 'vmod'); +export const { hcutoff, hpf, hp } = registerControl(['hcutoff', 'hresonance', 'hpenv'], 'hpf', 'hp'); +/** + * Controls the **h**igh-**p**ass **q**-value. + * + * @name hpq + * @param {number | Pattern} q resonance factor between 0 and 50 + * @synonyms hresonance + * @example + * s("bd sd [~ bd] sd,hh*8").hpf(2000).hpq("<0 10 20 30>") + * + */ +export const { hresonance, hpq } = registerControl('hresonance', 'hpq'); +/** + * Controls the **l**ow-**p**ass **q**-value. + * + * @name lpq + * @param {number | Pattern} q resonance factor between 0 and 50 + * @synonyms resonance + * @example + * s("bd sd [~ bd] sd,hh*8").lpf(2000).lpq("<0 10 20 30>") + * + */ +// currently an alias of 'resonance' https://github.com/tidalcycles/strudel/issues/496 +export const { resonance, lpq } = registerControl('resonance', 'lpq'); +/** + * 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 + * @example + * n("0 3 7 [10,24]").s('superzow').octave(3).djf("<.5 .25 .5 .75>").osc() + * + */ +export const { djf } = registerControl('djf'); +// ['cutoffegint'], +// TODO: does not seem to work +/** + * Sets the level of the delay signal. + * + * When using mininotation, you can also optionally add the 'delaytime' and 'delayfeedback' parameter, + * separated by ':'. + * + * + * @name delay + * @param {number | Pattern} level between 0 and 1 + * @example + * s("bd bd").delay("<0 .25 .5 1>") + * @example + * s("bd bd").delay("0.65:0.25:0.9 0.65:0.125:0.7") + * + */ +export const { delay } = registerControl(['delay', 'delaytime', 'delayfeedback']); +/** + * Sets the level of the signal that is fed back into the delay. + * Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it + * + * @name delayfeedback + * @param {number | Pattern} feedback between 0 and 1 + * @synonyms delayfb, dfb + * @example + * s("bd").delay(.25).delayfeedback("<.25 .5 .75 1>") + * + */ +export const { delayfeedback, delayfb, dfb } = registerControl('delayfeedback', 'delayfb', 'dfb'); +/** + * Sets the time of the delay effect. + * + * @name delaytime + * @param {number | Pattern} seconds between 0 and Infinity + * @synonyms delayt, dt + * @example + * s("bd bd").delay(.25).delaytime("<.125 .25 .5 1>") + * + */ +export const { delaytime, delayt, dt } = registerControl('delaytime', 'delayt', 'dt'); +/* // TODO: test + * Specifies whether delaytime is calculated relative to cps. + * + * @name lock + * @param {number | Pattern} enable When set to 1, delaytime is a direct multiple of a cycle. + * @example + * s("sd").delay().lock(1).osc() + * + */ +export const { lock } = registerControl('lock'); +/** + * Set detune of oscillators. Works only with some synths, see tidal doc + * + * @name detune + * @param {number | Pattern} amount between 0 and 1 + * @synonyms det + * @superdirtOnly + * @example + * n("0 3 7").s('superzow').octave(3).detune("<0 .25 .5 1 2>").osc() + * + */ +export const { detune, det } = registerControl('detune', 'det'); +/** + * Set dryness of reverb. See `room` and `size` for more information about reverb. + * + * @name dry + * @param {number | Pattern} dry 0 = wet, 1 = dry + * @example + * n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc() + * @superdirtOnly + * + */ +export const { dry } = registerControl('dry'); +// TODO: does not seem to do anything +/* + * Used when using `begin`/`end` or `chop`/`striate` and friends, to change the fade out time of the 'grain' envelope. + * + * @name fadeTime + * @param {number | Pattern} time between 0 and 1 + * @example + * s("oh*4").end(.1).fadeTime("<0 .2 .4 .8>").osc() + * + */ +export const { fadeTime, fadeOutTime } = registerControl('fadeTime', 'fadeOutTime'); +// TODO: see above +export const { fadeInTime } = registerControl('fadeInTime'); +/** + * Set frequency of sound. + * + * @name freq + * @param {number | Pattern} frequency in Hz. the audible range is between 20 and 20000 Hz + * @example + * freq("220 110 440 110").s("superzow").osc() + * @example + * freq("110".mul.out(".5 1.5 .6 [2 3]")).s("superzow").osc() + * + */ +export const { freq } = registerControl('freq'); +// pitch envelope +/** + * Attack time of pitch envelope. + * + * @name pattack + * @synonyms patt + * @param {number | Pattern} time time in seconds + * @example + * note("c eb g bb").pattack("0 .1 .25 .5").slow(2) + * + */ +export const { pattack, patt } = registerControl('pattack', 'patt'); +/** + * Decay time of pitch envelope. + * + * @name pdecay + * @synonyms pdec + * @param {number | Pattern} time time in seconds + * @example + * note("").pdecay("<0 .1 .25 .5>") + * + */ +export const { pdecay, pdec } = registerControl('pdecay', 'pdec'); +// TODO: how to use psustain?! +export const { psustain, psus } = registerControl('psustain', 'psus'); +/** + * Release time of pitch envelope + * + * @name prelease + * @synonyms prel + * @param {number | Pattern} time time in seconds + * @example + * note(" ~") + * .release(.5) // to hear the pitch release + * .prelease("<0 .1 .25 .5>") + * + */ +export const { prelease, prel } = registerControl('prelease', 'prel'); +/** + * Amount of pitch envelope. Negative values will flip the envelope. + * If you don't set other pitch envelope controls, `pattack:.2` will be the default. + * + * @name penv + * @param {number | Pattern} semitones change in semitones + * @example + * note("c") + * .penv("<12 7 1 .5 0 -1 -7 -12>") + * + */ +export const { penv } = registerControl('penv'); +/** + * Curve of envelope. Defaults to linear. exponential is good for kicks + * + * @name pcurve + * @param {number | Pattern} type 0 = linear, 1 = exponential + * @example + * note("g1*4") + * .s("sine").pdec(.5) + * .penv(32) + * .pcurve("<0 1>") + * + */ +export const { pcurve } = registerControl('pcurve'); +/** + * Sets the range anchor of the envelope: + * - anchor 0: range = [note, note + penv] + * - anchor 1: range = [note - penv, note] + * If you don't set an anchor, the value will default to the psustain value. + * + * @name panchor + * @param {number | Pattern} anchor anchor offset + * @example + * note("c c4").penv(12).panchor("<0 .5 1 .5>") + * + */ +export const { panchor } = registerControl('panchor'); +// TODO: https://tidalcycles.org/docs/configuration/MIDIOSC/control-voltage/#gate +export const { gate, gat } = registerControl('gate', 'gat'); +// ['hatgrain'], +// ['lagogo'], +// ['lclap'], +// ['lclaves'], +// ['lclhat'], +// ['lcrash'], +// TODO: +// https://tidalcycles.org/docs/reference/audio_effects/#leslie-1 +// https://tidalcycles.org/docs/reference/audio_effects/#leslie +/** + * Emulation of a Leslie speaker: speakers rotating in a wooden amplified cabinet. + * + * @name leslie + * @param {number | Pattern} wet between 0 and 1 + * @example + * n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc() + * @superdirtOnly + * + */ +export const { leslie } = registerControl('leslie'); +/** + * Rate of modulation / rotation for leslie effect + * + * @name lrate + * @param {number | Pattern} rate 6.7 for fast, 0.7 for slow + * @example + * n("0,4,7").s("supersquare").leslie(1).lrate("<1 2 4 8>").osc() + * @superdirtOnly + * + */ +// TODO: the rate seems to "lag" (in the example, 1 will be fast) +export const { lrate } = registerControl('lrate'); +/** + * Physical size of the cabinet in meters. Be careful, it might be slightly larger than your computer. Affects the Doppler amount (pitch warble) + * + * @name lsize + * @param {number | Pattern} meters somewhere between 0 and 1 + * @example + * n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc() + * @superdirtOnly + * + */ +export const { lsize } = registerControl('lsize'); +/** + * Sets the displayed text for an event on the pianoroll + * + * @name label + * @param {string} label text to display + */ +export const { activeLabel } = registerControl('activeLabel'); +export const { label } = registerControl(['label', 'activeLabel']); +// ['lfo'], +// ['lfocutoffint'], +// ['lfodelay'], +// ['lfoint'], +// ['lfopitchint'], +// ['lfoshape'], +// ['lfosync'], +// ['lhitom'], +// ['lkick'], +// ['llotom'], +// ['lophat'], +// ['lsnare'], +// TODO: what is this? not found in tidal doc +export const { degree } = registerControl('degree'); +// TODO: what is this? not found in tidal doc +export const { mtranspose } = registerControl('mtranspose'); +// TODO: what is this? not found in tidal doc +export const { ctranspose } = registerControl('ctranspose'); +// TODO: what is this? not found in tidal doc +export const { harmonic } = registerControl('harmonic'); +// TODO: what is this? not found in tidal doc +export const { stepsPerOctave } = registerControl('stepsPerOctave'); +// TODO: what is this? not found in tidal doc +export const { octaveR } = registerControl('octaveR'); +// TODO: why is this needed? what's the difference to late / early? Answer: it's in seconds, and delays the message at +// OSC time (so can't be negative, at least not beyond the latency value) +export const { nudge } = registerControl('nudge'); +// TODO: the following doc is just a guess, it's not documented in tidal doc. +/** + * Sets the default octave of a synth. + * + * @name octave + * @param {number | Pattern} octave octave number + * @example + * n("0,4,7").s('supersquare').octave("<3 4 5 6>").osc() + * @superDirtOnly + */ +export const { octave } = registerControl('octave'); + +// ['ophatdecay'], +// TODO: example +/** + * An `orbit` is a global parameter context for patterns. Patterns with the same orbit will share the same global effects. + * + * @name orbit + * @param {number | Pattern} number + * @example + * stack( + * s("hh*6").delay(.5).delaytime(.25).orbit(1), + * s("~ sd ~ sd").delay(.5).delaytime(.125).orbit(2) + * ) + */ +export const { orbit } = registerControl('orbit'); +// TODO: what is this? not found in tidal doc Answer: gain is limited to maximum of 2. This allows you to go over that +export const { overgain } = registerControl('overgain'); +// TODO: what is this? not found in tidal doc. Similar to above, but limited to 1 +export const { overshape } = registerControl('overshape'); +/** + * Sets position in stereo. + * + * @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>") + * @example + * s("bd rim sd rim bd ~ cp rim").pan(sine.slow(2)) + * + */ +export const { pan } = registerControl('pan'); +// TODO: this has no effect (see example) +/* + * Controls how much multichannel output is fanned out + * + * @name panspan + * @param {number | Pattern} span between -inf and inf, negative is backwards ordering + * @example + * s("[bd hh]*2").pan("<.5 1 .5 0>").panspan("<0 .5 1>").osc() + * + */ +export const { panspan } = registerControl('panspan'); +// TODO: this has no effect (see example) +/* + * Controls how much multichannel output is spread + * + * @name pansplay + * @param {number | Pattern} spread between 0 and 1 + * @example + * s("[bd hh]*2").pan("<.5 1 .5 0>").pansplay("<0 .5 1>").osc() + * + */ +export const { pansplay } = registerControl('pansplay'); +export const { panwidth } = registerControl('panwidth'); +export const { panorient } = registerControl('panorient'); +// ['pitch1'], +// ['pitch2'], +// ['pitch3'], +// ['portamento'], +// TODO: LFO rate see https://tidalcycles.org/docs/patternlib/tutorials/synthesizers/#supersquare +export const { rate } = registerControl('rate'); +// TODO: slide param for certain synths +export const { slide } = registerControl('slide'); +// TODO: detune? https://tidalcycles.org/docs/patternlib/tutorials/synthesizers/#supersquare +export const { semitone } = registerControl('semitone'); +// TODO: dedup with synth param, see https://tidalcycles.org/docs/reference/synthesizers/#superpiano +// ['velocity'], +// TODO: synth param +export const { voice } = registerControl('voice'); + +// voicings // https://github.com/tidalcycles/strudel/issues/506 +// chord to voice, like C Eb Fm7 G7. the symbols can be defined via addVoicings +export const { chord } = registerControl('chord'); +// which dictionary to use for the voicings +export const { dictionary, dict } = registerControl('dictionary', 'dict'); +// the top note to align the voicing to, defaults to c5 +export const { anchor } = registerControl('anchor'); +// how the voicing is offset from the anchored position +export const { offset } = registerControl('offset'); +// how many octaves are voicing steps spread apart, defaults to 1 +export const { octaves } = registerControl('octaves'); +// below = anchor note will be removed from the voicing, useful for melody harmonization +export const { mode } = registerControl(['mode', 'anchor']); + +/** + * Sets the level of reverb. + * + * When using mininotation, you can also optionally add the 'size' parameter, separated by ':'. + * + * @name room + * @param {number | Pattern} level between 0 and 1 + * @example + * s("bd sd [~ bd] sd").room("<0 .2 .4 .6 .8 1>") + * @example + * s("bd sd [~ bd] sd").room("<0.9:1 0.9:4>") + * + */ +export const { room } = registerControl(['room', 'size']); +/** + * Reverb lowpass starting frequency (in hertz). + * When this property is changed, the reverb will be recaculated, so only change this sparsely.. + * + * @name roomlp + * @synonyms rlp + * @param {number} frequency between 0 and 20000hz + * @example + * s("bd sd [~ bd] sd").room(0.5).rlp(10000) + * @example + * s("bd sd [~ bd] sd").room(0.5).rlp(5000) + */ +export const { roomlp, rlp } = registerControl('roomlp', 'rlp'); +/** + * Reverb lowpass frequency at -60dB (in hertz). + * When this property is changed, the reverb will be recaculated, so only change this sparsely.. + * + * @name roomdim + * @synonyms rdim + * @param {number} frequency between 0 and 20000hz + * @example + * s("bd sd [~ bd] sd").room(0.5).rlp(10000).rdim(8000) + * @example + * s("bd sd [~ bd] sd").room(0.5).rlp(5000).rdim(400) + * + */ +export const { roomdim, rdim } = registerControl('roomdim', 'rdim'); +/** + * Reverb fade time (in seconds). + * When this property is changed, the reverb will be recaculated, so only change this sparsely.. + * + * @name roomfade + * @synonyms rfade + * @param {number} seconds for the reverb to fade + * @example + * s("bd sd [~ bd] sd").room(0.5).rlp(10000).rfade(0.5) + * @example + * s("bd sd [~ bd] sd").room(0.5).rlp(5000).rfade(4) + * + */ +export const { roomfade, rfade } = registerControl('roomfade', 'rfade'); +/** + * Sets the sample to use as an impulse response for the reverb. + * @name iresponse + * @param {string | Pattern} sample to use as an impulse response + * @synonyms ir + * @example + * s("bd sd [~ bd] sd").room(.8).ir("") + * + */ +export const { ir, iresponse } = registerControl(['ir', 'i'], 'iresponse'); +/** + * Sets the room size of the reverb, see `room`. + * When this property is changed, the reverb will be recaculated, so only change this sparsely.. + * + * @name roomsize + * @param {number | Pattern} size between 0 and 10 + * @synonyms rsize, sz, size + * @example + * s("bd sd [~ bd] sd").room(.8).rsize(1) + * @example + * s("bd sd [~ bd] sd").room(.8).rsize(4) + * + */ +// TODO: find out why : +// s("bd sd [~ bd] sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc() +// .. does not work. Is it because room is only one effect? +export const { roomsize, size, sz, rsize } = registerControl('roomsize', 'size', 'sz', 'rsize'); +// ['sagogo'], +// ['sclap'], +// ['sclaves'], +// ['scrash'], +/** + * Wave shaping distortion. CAUTION: it might get loud + * + * @name shape + * @param {number | Pattern} distortion between 0 and 1 + * @example + * s("bd sd [~ bd] sd,hh*8").shape("<0 .2 .4 .6 .8>") + * + */ +export const { shape } = registerControl('shape'); +/** + * 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) + * + * @name compressor + * @example + * s("bd sd [~ bd] sd,hh*8") + * .compressor("-20:20:10:.002:.02") + * + */ +export const { compressor } = registerControl([ + 'compressor', + 'compressorRatio', + 'compressorKnee', + 'compressorAttack', + 'compressorRelease', +]); +export const { compressorKnee } = registerControl('compressorKnee'); +export const { compressorRatio } = registerControl('compressorRatio'); +export const { compressorAttack } = registerControl('compressorAttack'); +export const { compressorRelease } = registerControl('compressorRelease'); +/** + * Changes the speed of sample playback, i.e. a cheap way of changing pitch. + * + * @name speed + * @param {number | Pattern} speed -inf to inf, negative numbers play the sample backwards. + * @example + * s("bd*6").speed("1 2 4 1 -2 -4") + * @example + * speed("1 1.5*2 [2 1.1]").s("piano").clip(1) + * + */ +export const { speed } = registerControl('speed'); +/** + * Used in conjunction with `speed`, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`. + * + * @name unit + * @param {number | string | Pattern} unit see description above + * @example + * speed("1 2 .5 3").s("bd").unit("c").osc() + * @superdirtOnly + * + */ +export const { unit } = registerControl('unit'); +/** + * Made by Calum Gunn. Reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter. The SuperCollider manual defines Squiz as: + * + * "A simplistic pitch-raising algorithm. It's not meant to sound natural; its sound is reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter, depending on the input. The algorithm works by cutting the signal into fragments (delimited by upwards-going zero-crossings) and squeezing those fragments in the time domain (i.e. simply playing them back faster than they came in), leaving silences inbetween. All the parameters apart from memlen can be modulated." + * + * @name squiz + * @param {number | Pattern} squiz Try passing multiples of 2 to it - 2, 4, 8 etc. + * @example + * squiz("2 4/2 6 [8 16]").s("bd").osc() + * @superdirtOnly + * + */ +export const { squiz } = registerControl('squiz'); +// TODO: what is this? not found in tidal doc +// ['stutterdepth'], +// TODO: what is this? not found in tidal doc +// ['stuttertime'], +// TODO: what is this? not found in tidal doc +// ['timescale'], +// TODO: what is this? not found in tidal doc +// ['timescalewin'], +// ['tomdecay'], +// ['vcfegint'], +// ['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 ae aa oe ue y uh un en an on, corresponding to [a] [e] [i] [o] [u] [æ] [ɑ] [ø] [y] [ɯ] [ʌ] [œ̃] [ɛ̃] [ɑ̃] [ɔ̃]. Aliases: aa = å = ɑ, oe = ø = ö, y = ı, ae = æ. + * @example + * note("[c2 >]*2").s('sawtooth') + * .vowel(">") + * @example + * s("bd sd mt ht bd [~ cp] ht lt").vowel("[a|e|i|o|u]") + * + */ +export const { vowel } = registerControl('vowel'); +/* // TODO: find out how it works + * Made by Calum Gunn. Divides an audio stream into tiny segments, using the signal's zero-crossings as segment boundaries, and discards a fraction of them. Takes a number between 1 and 100, denoted the percentage of segments to drop. The SuperCollider manual describes the Waveloss effect this way: + * + * Divide an audio stream into tiny segments, using the signal's zero-crossings as segment boundaries, and discard a fraction of them (i.e. replace them with silence of the same length). The technique was described by Trevor Wishart in a lecture. Parameters: the filter drops drop out of out of chunks. mode can be 1 to drop chunks in a simple deterministic fashion (e.g. always dropping the first 30 out of a set of 40 segments), or 2 to drop chunks randomly but in an appropriate proportion.) + * + * mode: ? + * waveloss: ? + * + * @name waveloss + */ +export const { waveloss } = registerControl('waveloss'); +/* + * Noise crackle density + * + * @name density + * @param {number | Pattern} density between 0 and x + * @example + * s("crackle*4").density("<0.01 0.04 0.2 0.5>".slow(4)) + * + */ +export const { density } = registerControl('density'); +// TODO: midi effects? +export const { dur } = registerControl('dur'); +// ['modwheel'], +export const { expression } = registerControl('expression'); +export const { sustainpedal } = registerControl('sustainpedal'); +/* // TODO: doesn't seem to do anything + * + * Tremolo Audio DSP effect + * + * @name tremolodepth + * @param {number | Pattern} depth between 0 and 1 + * @example + * n("0,4,7").tremolodepth("<0 .3 .6 .9>").osc() + * + */ +export const { tremolodepth, tremdp } = registerControl('tremolodepth', 'tremdp'); +export const { tremolorate, tremr } = registerControl('tremolorate', 'tremr'); + +export const { fshift } = registerControl('fshift'); +export const { fshiftnote } = registerControl('fshiftnote'); +export const { fshiftphase } = registerControl('fshiftphase'); + +export const { triode } = registerControl('triode'); +export const { krush } = registerControl('krush'); +export const { kcutoff } = registerControl('kcutoff'); +export const { octer } = registerControl('octer'); +export const { octersub } = registerControl('octersub'); +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'); +export const { real } = registerControl('real'); +export const { imag } = registerControl('imag'); +export const { enhance } = registerControl('enhance'); +export const { partials } = registerControl('partials'); +export const { comb } = registerControl('comb'); +export const { smear } = registerControl('smear'); +export const { scram } = registerControl('scram'); +export const { binshift } = registerControl('binshift'); +export const { hbrick } = registerControl('hbrick'); +export const { lbrick } = registerControl('lbrick'); +export const { midichan } = registerControl('midichan'); +export const { control } = registerControl('control'); +export const { ccn } = registerControl('ccn'); +export const { ccv } = registerControl('ccv'); +export const { polyTouch } = registerControl('polyTouch'); +export const { midibend } = registerControl('midibend'); +export const { miditouch } = registerControl('miditouch'); +export const { ctlNum } = registerControl('ctlNum'); +export const { frameRate } = registerControl('frameRate'); +export const { frames } = registerControl('frames'); +export const { hours } = registerControl('hours'); +export const { midicmd } = registerControl('midicmd'); +export const { minutes } = registerControl('minutes'); +export const { progNum } = registerControl('progNum'); +export const { seconds } = registerControl('seconds'); +export const { songPtr } = registerControl('songPtr'); +export const { uid } = registerControl('uid'); +export const { val } = registerControl('val'); +export const { cps } = registerControl('cps'); +/** + * Multiplies the duration with the given number. Also cuts samples off at the end if they exceed the duration. + * In tidal, this would be done with legato, [which has a complicated history in strudel](https://github.com/tidalcycles/strudel/issues/111). + * For now, if you're coming from tidal, just think clip = legato. + * + * @name clip + * @param {number | Pattern} factor >= 0 + * @example + * note("c a f e").s("piano").clip("<.5 1 2>") + * + */ +export const { clip } = registerControl('clip'); + +// ZZFX +export const { zrand } = registerControl('zrand'); +export const { curve } = registerControl('curve'); +// superdirt duplicate +// export const {slide]} = registerControl('slide']); +export const { deltaSlide } = registerControl('deltaSlide'); +export const { pitchJump } = registerControl('pitchJump'); +export const { pitchJumpTime } = registerControl('pitchJumpTime'); +export const { lfo, repeatTime } = registerControl('lfo', 'repeatTime'); +// noise on the frequency or as bubo calls it "frequency fog" :) +export const { znoise } = registerControl('znoise'); +export const { zmod } = registerControl('zmod'); +// like crush but scaled differently +export const { zcrush } = registerControl('zcrush'); +export const { zdelay } = registerControl('zdelay'); +export const { tremolo } = registerControl('tremolo'); +export const { zzfx } = registerControl('zzfx'); + +// TODO: slice / splice https://www.youtube.com/watch?v=hKhPdO0RKDQ&list=PL2lW1zNIIwj3bDkh-Y3LUGDuRcoUigoDs&index=13 + +export let createParams = (...names) => + names.reduce((acc, name) => Object.assign(acc, { [name]: createParam(name) }), {}); /** * ADSR envelope: Combination of Attack, Decay, Sustain, and Release. @@ -1484,25 +1500,23 @@ controls.createParams = (...names) => * @example * note("[c3 bb2 f3 eb3]*2").sound("sawtooth").lpf(600).adsr(".1:.1:.5:.2") */ -controls.adsr = register('adsr', (adsr, pat) => { +export const adsr = register('adsr', (adsr, pat) => { adsr = !Array.isArray(adsr) ? [adsr] : adsr; const [attack, decay, sustain, release] = adsr; return pat.set({ attack, decay, sustain, release }); }); -controls.ad = register('ad', (t, pat) => { +export const ad = register('ad', (t, pat) => { t = !Array.isArray(t) ? [t] : t; const [attack, decay = attack] = t; return pat.attack(attack).decay(decay); }); -controls.ds = register('ds', (t, pat) => { +export const ds = register('ds', (t, pat) => { t = !Array.isArray(t) ? [t] : t; const [decay, sustain = 0] = t; return pat.set({ decay, sustain }); }); -controls.ds = register('ar', (t, pat) => { +export const ar = register('ar', (t, pat) => { t = !Array.isArray(t) ? [t] : t; const [attack, release = attack] = t; return pat.set({ attack, release }); }); - -export default controls; From da674d3e05cabf2010a6c230488117ca328bd404 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Wed, 28 Feb 2024 17:26:59 -0500 Subject: [PATCH 09/24] updated controls --- packages/core/controls.mjs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 42fa8cba..1f49cf06 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1276,7 +1276,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,6 +1288,21 @@ export const { roomsize, size, sz, rsize } = registerControl('roomsize', 'size', * */ export const { shape } = registerControl('shape'); +/** + * 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', '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) @@ -1420,7 +1438,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'); From fba2422a64aa1e7358c85a9f821f3f1e9d40da5f Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Wed, 28 Feb 2024 17:32:30 -0500 Subject: [PATCH 10/24] update test --- test/__snapshots__/examples.test.mjs.snap | 90 +++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 0c28f04b..d6c0fe70 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 0.5] ]", + "[ 3/1 → 13/4 | s:bd distort:[10 0.5] ]", + "[ 25/8 → 13/4 | s:hh distort:[10 0.5] ]", + "[ 13/4 → 27/8 | s:hh distort:[10 0.5] ]", + "[ 13/4 → 7/2 | s:sd distort:[10 0.5] ]", + "[ 27/8 → 7/2 | s:hh distort:[10 0.5] ]", + "[ 7/2 → 29/8 | s:hh distort:[10 0.5] ]", + "[ 29/8 → 15/4 | s:bd distort:[10 0.5] ]", + "[ 29/8 → 15/4 | s:hh distort:[10 0.5] ]", + "[ 15/4 → 31/8 | s:hh distort:[10 0.5] ]", + "[ 15/4 → 4/1 | s:sd distort:[10 0.5] ]", + "[ 31/8 → 4/1 | s:hh distort:[10 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 0.4] ]", + "[ 1/8 → 1/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 1/4 → 3/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 3/8 → 1/2 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 1/2 → 5/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 5/8 → 3/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 3/4 → 7/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 7/8 → 1/1 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 1/1 → 9/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 9/8 → 5/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 5/4 → 11/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 11/8 → 3/2 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 3/2 → 13/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 13/8 → 7/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 7/4 → 15/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 15/8 → 2/1 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 2/1 → 17/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 17/8 → 9/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 9/4 → 19/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 19/8 → 5/2 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 5/2 → 21/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 21/8 → 11/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 11/4 → 23/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 23/8 → 3/1 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 3/1 → 25/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 25/8 → 13/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 13/4 → 27/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 27/8 → 7/2 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 7/2 → 29/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 29/8 → 15/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 15/4 → 31/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 31/8 → 4/1 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", +] +`; + exports[`runs examples > example "djf" example index 0 1`] = ` [ "[ 0/1 → 1/4 | n:0 s:superzow octave:3 djf:0.5 ]", From 94dd547a74d7ce9dc5c353adb951f6e2507b50fb Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 29 Feb 2024 21:56:31 +0100 Subject: [PATCH 11/24] canvas package boilerplate --- packages/canvas/README.md | 9 +++ packages/canvas/index.mjs | 0 packages/canvas/package.json | 37 ++++++++++++ packages/canvas/vite.config.js | 19 +++++++ pnpm-lock.yaml | 101 ++++++++++++++++++++++++--------- 5 files changed, 138 insertions(+), 28 deletions(-) create mode 100644 packages/canvas/README.md create mode 100644 packages/canvas/index.mjs create mode 100644 packages/canvas/package.json create mode 100644 packages/canvas/vite.config.js diff --git a/packages/canvas/README.md b/packages/canvas/README.md new file mode 100644 index 00000000..e144d85b --- /dev/null +++ b/packages/canvas/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/canvas/index.mjs b/packages/canvas/index.mjs new file mode 100644 index 00000000..e69de29b diff --git a/packages/canvas/package.json b/packages/canvas/package.json new file mode 100644 index 00000000..f87a7e19 --- /dev/null +++ b/packages/canvas/package.json @@ -0,0 +1,37 @@ +{ + "name": "@strudel/canvas", + "version": "1.0.1", + "description": "Helpers for drawing with the Canvas API and 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/canvas/vite.config.js b/packages/canvas/vite.config.js new file mode 100644 index 00000000..5df3edc1 --- /dev/null +++ b/packages/canvas/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/pnpm-lock.yaml b/pnpm-lock.yaml index 4b31cc8b..9f6da70d 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,17 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 + + packages/canvas: + dependencies: + '@strudel/core': + specifier: workspace:* + version: link:../core + devDependencies: + vite: + specifier: ^5.0.10 + version: 5.0.11 packages/codemirror: dependencies: @@ -193,7 +203,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 +213,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 +232,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/desktopbridge: dependencies: @@ -249,7 +259,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 +275,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 +288,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 +307,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: @@ -337,7 +347,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 +357,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 +379,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 +389,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 +408,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 +433,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,7 +461,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/webaudio: dependencies: @@ -464,7 +474,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/xen: dependencies: @@ -474,7 +484,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) @@ -5346,8 +5356,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 @@ -13535,7 +13545,7 @@ packages: debug: 4.3.4 pathe: 1.1.1 picocolors: 1.0.0 - vite: 5.0.11(@types/node@20.10.6) + vite: 5.0.11 transitivePeerDependencies: - '@types/node' - less @@ -13565,7 +13575,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 +13603,47 @@ 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: + resolution: {integrity: sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + esbuild: 0.19.11 + 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==} @@ -13635,7 +13680,7 @@ packages: 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 +13688,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==} @@ -13689,7 +13734,7 @@ packages: strip-literal: 1.3.0 tinybench: 2.5.1 tinypool: 0.8.1 - vite: 5.0.11(@types/node@20.10.6) + vite: 5.0.11 vite-node: 1.1.0 why-is-node-running: 2.2.2 transitivePeerDependencies: From 6ce8a3febadbe9426f621ffb96ded8645ed4792d Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 29 Feb 2024 22:14:53 +0100 Subject: [PATCH 12/24] move draw logic to separate package --- packages/{core => canvas}/animate.mjs | 7 ++- packages/{core => canvas}/color.mjs | 0 packages/{core => canvas}/draw.mjs | 6 +- packages/canvas/index.mjs | 5 ++ packages/{core => canvas}/pianoroll.mjs | 4 +- packages/{core => canvas}/spiral.mjs | 2 +- packages/codemirror/codemirror.mjs | 3 +- packages/codemirror/package.json | 1 + packages/core/index.mjs | 4 -- packages/hydra/hydra.mjs | 2 +- packages/hydra/package.json | 1 + packages/repl/package.json | 1 + packages/repl/prebake.mjs | 1 + packages/repl/repl-component.mjs | 3 +- packages/webaudio/package.json | 1 + packages/webaudio/scope.mjs | 3 +- pnpm-lock.yaml | 81 +++++++++++++------------ website/package.json | 1 + website/src/docs/Colors.jsx | 3 +- website/src/repl/Repl.jsx | 3 +- website/src/repl/drawings.mjs | 4 +- website/src/repl/util.mjs | 1 + 22 files changed, 78 insertions(+), 59 deletions(-) rename packages/{core => canvas}/animate.mjs (89%) rename packages/{core => canvas}/color.mjs (100%) rename packages/{core => canvas}/draw.mjs (97%) rename packages/{core => canvas}/pianoroll.mjs (98%) rename packages/{core => canvas}/spiral.mjs (98%) diff --git a/packages/core/animate.mjs b/packages/canvas/animate.mjs similarity index 89% rename from packages/core/animate.mjs rename to packages/canvas/animate.mjs index cc7e59b2..d8508151 100644 --- a/packages/core/animate.mjs +++ b/packages/canvas/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/canvas/color.mjs similarity index 100% rename from packages/core/color.mjs rename to packages/canvas/color.mjs diff --git a/packages/core/draw.mjs b/packages/canvas/draw.mjs similarity index 97% rename from packages/core/draw.mjs rename to packages/canvas/draw.mjs index 30de7ba8..9d0e0ac8 100644 --- a/packages/core/draw.mjs +++ b/packages/canvas/draw.mjs @@ -1,15 +1,15 @@ /* 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') => { let canvas = document.querySelector('#' + id); if (!canvas) { - const scale = 2; // 2 = crisp on retina screens + const scale = window.devicePixelRatio || 1; canvas = document.createElement('canvas'); canvas.id = id; canvas.width = window.innerWidth * scale; diff --git a/packages/canvas/index.mjs b/packages/canvas/index.mjs index e69de29b..89cda805 100644 --- a/packages/canvas/index.mjs +++ b/packages/canvas/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/core/pianoroll.mjs b/packages/canvas/pianoroll.mjs similarity index 98% rename from packages/core/pianoroll.mjs rename to packages/canvas/pianoroll.mjs index de6fc72b..d408f44a 100644 --- a/packages/core/pianoroll.mjs +++ b/packages/canvas/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/canvas/spiral.mjs similarity index 98% rename from packages/core/spiral.mjs rename to packages/canvas/spiral.mjs index e0d5cd87..00bd62ec 100644 --- a/packages/core/spiral.mjs +++ b/packages/canvas/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/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index 4470173b..3209ef87 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/canvas'; 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..79d2e022 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/canvas": "workspace:*", "@uiw/codemirror-themes": "^4.21.21", "@uiw/codemirror-themes-all": "^4.21.21", "nanostores": "^0.9.5" 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/hydra/hydra.mjs b/packages/hydra/hydra.mjs index dc1ba3f9..b7912446 100644 --- a/packages/hydra/hydra.mjs +++ b/packages/hydra/hydra.mjs @@ -1,4 +1,4 @@ -import { getDrawContext } from '@strudel/core'; +import { getDrawContext } from '@strudel/canvas'; let latestOptions; diff --git a/packages/hydra/package.json b/packages/hydra/package.json index 48189663..c635314c 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/canvas": "workspace:*", "hydra-synth": "^1.3.29" }, "devDependencies": { diff --git a/packages/repl/package.json b/packages/repl/package.json index e6bce6d5..742c5bb7 100644 --- a/packages/repl/package.json +++ b/packages/repl/package.json @@ -35,6 +35,7 @@ "dependencies": { "@strudel/codemirror": "workspace:*", "@strudel/core": "workspace:*", + "@strudel/canvas": "workspace:*", "@strudel/hydra": "workspace:*", "@strudel/midi": "workspace:*", "@strudel/mini": "workspace:*", diff --git a/packages/repl/prebake.mjs b/packages/repl/prebake.mjs index 4315b85c..1858580b 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/canvas'), 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..4bdd6fe6 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/canvas'; import { transpiler } from '@strudel/transpiler'; import { getAudioContext, webaudioOutput } from '@strudel/webaudio'; import { StrudelMirror, codemirrorSettings } from '@strudel/codemirror'; diff --git a/packages/webaudio/package.json b/packages/webaudio/package.json index dce53961..29348f48 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/canvas": "workspace:*", "superdough": "workspace:*" }, "devDependencies": { diff --git a/packages/webaudio/scope.mjs b/packages/webaudio/scope.mjs index 115f159a..288880c0 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 '@strudel/canvas'; import { analyser, getAnalyzerData } from 'superdough'; export function drawTimeScope( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9f6da70d..1ef01efc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -148,7 +148,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.11 + version: 5.0.11(@types/node@20.10.6) packages/codemirror: dependencies: @@ -188,6 +188,9 @@ importers: '@replit/codemirror-vscode-keymap': specifier: ^6.0.2 version: 6.0.2(@codemirror/autocomplete@6.11.1)(@codemirror/commands@6.3.3)(@codemirror/language@6.10.0)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.5)(@codemirror/state@6.4.0)(@codemirror/view@6.23.0) + '@strudel/canvas': + specifier: workspace:* + version: link:../canvas '@strudel/core': specifier: workspace:* version: link:../core @@ -247,6 +250,9 @@ importers: packages/hydra: dependencies: + '@strudel/canvas': + specifier: workspace:* + version: link:../canvas '@strudel/core': specifier: workspace:* version: link:../core @@ -311,6 +317,9 @@ importers: packages/repl: dependencies: + '@strudel/canvas': + specifier: workspace:* + version: link:../canvas '@strudel/codemirror': specifier: workspace:* version: link:../codemirror @@ -465,6 +474,9 @@ importers: packages/webaudio: dependencies: + '@strudel/canvas': + specifier: workspace:* + version: link:../canvas '@strudel/core': specifier: workspace:* version: link:../core @@ -533,6 +545,9 @@ importers: '@nanostores/react': specifier: ^0.7.1 version: 0.7.1(nanostores@0.9.5)(react@18.2.0) + '@strudel/canvas': + specifier: workspace:* + version: link:../packages/canvas '@strudel/codemirror': specifier: workspace:* version: link:../packages/codemirror @@ -2392,6 +2407,7 @@ packages: cpu: [arm64] os: [android] requiresBuild: true + dev: true optional: true /@esbuild/android-arm@0.19.11: @@ -2408,6 +2424,7 @@ packages: cpu: [arm] os: [android] requiresBuild: true + dev: true optional: true /@esbuild/android-x64@0.19.11: @@ -2424,6 +2441,7 @@ packages: cpu: [x64] os: [android] requiresBuild: true + dev: true optional: true /@esbuild/darwin-arm64@0.19.11: @@ -2440,6 +2458,7 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true + dev: true optional: true /@esbuild/darwin-x64@0.19.11: @@ -2456,6 +2475,7 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true + dev: true optional: true /@esbuild/freebsd-arm64@0.19.11: @@ -2472,6 +2492,7 @@ packages: cpu: [arm64] os: [freebsd] requiresBuild: true + dev: true optional: true /@esbuild/freebsd-x64@0.19.11: @@ -2488,6 +2509,7 @@ packages: cpu: [x64] os: [freebsd] requiresBuild: true + dev: true optional: true /@esbuild/linux-arm64@0.19.11: @@ -2504,6 +2526,7 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-arm@0.19.11: @@ -2520,6 +2543,7 @@ packages: cpu: [arm] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-ia32@0.19.11: @@ -2536,6 +2560,7 @@ packages: cpu: [ia32] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-loong64@0.19.11: @@ -2552,6 +2577,7 @@ packages: cpu: [loong64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-mips64el@0.19.11: @@ -2568,6 +2594,7 @@ packages: cpu: [mips64el] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-ppc64@0.19.11: @@ -2584,6 +2611,7 @@ packages: cpu: [ppc64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-riscv64@0.19.11: @@ -2600,6 +2628,7 @@ packages: cpu: [riscv64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-s390x@0.19.11: @@ -2616,6 +2645,7 @@ packages: cpu: [s390x] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-x64@0.19.11: @@ -2632,6 +2662,7 @@ packages: cpu: [x64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/netbsd-x64@0.19.11: @@ -2648,6 +2679,7 @@ packages: cpu: [x64] os: [netbsd] requiresBuild: true + dev: true optional: true /@esbuild/openbsd-x64@0.19.11: @@ -2664,6 +2696,7 @@ packages: cpu: [x64] os: [openbsd] requiresBuild: true + dev: true optional: true /@esbuild/sunos-x64@0.19.11: @@ -2680,6 +2713,7 @@ packages: cpu: [x64] os: [sunos] requiresBuild: true + dev: true optional: true /@esbuild/win32-arm64@0.19.11: @@ -2696,6 +2730,7 @@ packages: cpu: [arm64] os: [win32] requiresBuild: true + dev: true optional: true /@esbuild/win32-ia32@0.19.11: @@ -2712,6 +2747,7 @@ packages: cpu: [ia32] os: [win32] requiresBuild: true + dev: true optional: true /@esbuild/win32-x64@0.19.11: @@ -2728,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): @@ -6838,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==} @@ -13545,7 +13583,7 @@ packages: debug: 4.3.4 pathe: 1.1.1 picocolors: 1.0.0 - vite: 5.0.11 + vite: 5.0.11(@types/node@20.10.6) transitivePeerDependencies: - '@types/node' - less @@ -13610,41 +13648,6 @@ packages: fsevents: 2.3.3 dev: true - /vite@5.0.11: - resolution: {integrity: sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - dependencies: - esbuild: 0.19.11 - 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==} engines: {node: ^18.0.0 || >=20.0.0} @@ -13674,7 +13677,7 @@ 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: @@ -13734,7 +13737,7 @@ packages: strip-literal: 1.3.0 tinybench: 2.5.1 tinypool: 0.8.1 - vite: 5.0.11 + vite: 5.0.11(@types/node@20.10.6) vite-node: 1.1.0 why-is-node-running: 2.2.2 transitivePeerDependencies: diff --git a/website/package.json b/website/package.json index af100889..ed40b005 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/canvas": "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..f11a7440 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/canvas'; const Colors = () => { return ( diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index c5a8fa09..fd5892bd 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/canvas'; import cx from '@src/cx.mjs'; import { transpiler } from '@strudel/transpiler'; import { 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..d9c80ca9 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/canvas'), import('@strudel/tonal'), import('@strudel/mini'), import('@strudel/xen'), From 416a03aea1e4f398749c9515cd29b62bc320cf63 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Thu, 29 Feb 2024 18:09:26 -0500 Subject: [PATCH 13/24] updated params --- packages/core/controls.mjs | 4 ++-- packages/superdough/superdough.mjs | 12 ++++-------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 1f49cf06..bbc9a5bd 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1287,7 +1287,7 @@ 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. @@ -1302,7 +1302,7 @@ export const { shape } = registerControl('shape'); * note("d1!8").s("sine").penv(36).pdecay(.12).decay(.23).distort("8:.4") * */ -export const { distort, dist } = registerControl('distort', 'dist'); +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) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 83499f01..878e6c1e 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -316,7 +316,9 @@ export const superdough = async (value, deadline, hapDuration) => { coarse, crush, shape, + shapevol, distort, + distortvol, pan, vowel, delay = 0, @@ -458,14 +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 })); - if (shape !== undefined) { - const input = Array.isArray(shape) ? { shape: shape[0], postgain: shape[1] } : { shape }; - chain.push(getWorklet(ac, 'shape-processor', input)); - } - if (distort !== undefined) { - const input = Array.isArray(distort) ? { distort: distort[0], postgain: distort[1] } : { distort }; - chain.push(getWorklet(ac, 'distort-processor', input)); - } + 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( From c9b18f6fa44fc7ca03d8a0be1d45cb2e10c55412 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Thu, 29 Feb 2024 18:15:30 -0500 Subject: [PATCH 14/24] fix test --- test/__snapshots__/examples.test.mjs.snap | 88 +++++++++++------------ 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index d6c0fe70..ac0f47e9 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1971,55 +1971,55 @@ exports[`runs examples > example "distort" example index 0 1`] = ` "[ 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 0.5] ]", - "[ 3/1 → 13/4 | s:bd distort:[10 0.5] ]", - "[ 25/8 → 13/4 | s:hh distort:[10 0.5] ]", - "[ 13/4 → 27/8 | s:hh distort:[10 0.5] ]", - "[ 13/4 → 7/2 | s:sd distort:[10 0.5] ]", - "[ 27/8 → 7/2 | s:hh distort:[10 0.5] ]", - "[ 7/2 → 29/8 | s:hh distort:[10 0.5] ]", - "[ 29/8 → 15/4 | s:bd distort:[10 0.5] ]", - "[ 29/8 → 15/4 | s:hh distort:[10 0.5] ]", - "[ 15/4 → 31/8 | s:hh distort:[10 0.5] ]", - "[ 15/4 → 4/1 | s:sd distort:[10 0.5] ]", - "[ 31/8 → 4/1 | s:hh distort:[10 0.5] ]", + "[ 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 0.4] ]", - "[ 1/8 → 1/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 1/4 → 3/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 3/8 → 1/2 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 1/2 → 5/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 5/8 → 3/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 3/4 → 7/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 7/8 → 1/1 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 1/1 → 9/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 9/8 → 5/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 5/4 → 11/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 11/8 → 3/2 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 3/2 → 13/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 13/8 → 7/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 7/4 → 15/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 15/8 → 2/1 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 2/1 → 17/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 17/8 → 9/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 9/4 → 19/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 19/8 → 5/2 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 5/2 → 21/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 21/8 → 11/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 11/4 → 23/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 23/8 → 3/1 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 3/1 → 25/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 25/8 → 13/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 13/4 → 27/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 27/8 → 7/2 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 7/2 → 29/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 29/8 → 15/4 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 15/4 → 31/8 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", - "[ 31/8 → 4/1 | note:d1 s:sine penv:36 pdecay:0.12 decay:0.23 distort:[8 0.4] ]", + "[ 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 ]", ] `; From 4f94de86db5e15b4e9b6cfd963def175df847512 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 1 Mar 2024 13:09:14 +0100 Subject: [PATCH 15/24] fix: migrate more imports --- website/src/docs/MiniRepl.jsx | 3 ++- website/src/pages/img/example-[name].png.js | 2 +- website/src/pages/swatch/[name].png.js | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/website/src/docs/MiniRepl.jsx b/website/src/docs/MiniRepl.jsx index 3afb609e..9a886109 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/canvas'; 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..1361df79 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/canvas'; import { evaluate } from '@strudel/transpiler'; import '../../../../test/runtime.mjs'; import * as tunes from '../../repl/tunes.mjs'; diff --git a/website/src/pages/swatch/[name].png.js b/website/src/pages/swatch/[name].png.js index c0a52a6b..809a64ec 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/canvas'; import { evaluate } from '@strudel/transpiler'; import '../../../../test/runtime.mjs'; import { getMyPatterns } from '../../my_patterns'; From 62d8955da985f36b3aef493a0a45c098590b80b2 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 1 Mar 2024 16:46:34 +0100 Subject: [PATCH 16/24] onFrame function --- packages/canvas/draw.mjs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/canvas/draw.mjs b/packages/canvas/draw.mjs index 9d0e0ac8..b03634c5 100644 --- a/packages/canvas/draw.mjs +++ b/packages/canvas/draw.mjs @@ -61,6 +61,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) { + if (typeof window === 'undefined') { + return this; + } + if (window.strudelAnimation) { + cancelAnimationFrame(window.strudelAnimation); + } + const animate = () => { + const t = getTime(); + 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); From daa62202fad99b2a3499ecaae6b7538062287c95 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Fri, 1 Mar 2024 13:12:52 -0500 Subject: [PATCH 17/24] default values needed in superdough for worklet input --- packages/superdough/superdough.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 878e6c1e..ef62118e 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -316,9 +316,9 @@ export const superdough = async (value, deadline, hapDuration) => { coarse, crush, shape, - shapevol, + shapevol = 1, distort, - distortvol, + distortvol = 1, pan, vowel, delay = 0, From 4af7262f45a4b41267adec38d3f534534c09ea6e Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 2 Mar 2024 01:16:41 -0500 Subject: [PATCH 18/24] fixed performance issue --- packages/superdough/worklets.mjs | 99 +++++++++++++++++++------------- 1 file changed, 59 insertions(+), 40 deletions(-) diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 3f6d7ac9..4f2965ee 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -1,23 +1,6 @@ -const processSample = (inputs, outputs, processBlock) => { - const input = inputs[0]; - const output = outputs[0]; - const blockSize = 128; - if (input == null || output == null) { - return false; - } - - for (let n = 0; n < blockSize; n++) { - input.forEach((inChannel, i) => { - const outChannel = output[i % output.length]; - const block = inChannel[n]; - outChannel[n] = processBlock(block, n, inChannel, outChannel); - }); - } - return true; -}; - // 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 + class CoarseProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { return [{ name: 'coarse', defaultValue: 1 }]; @@ -28,15 +11,24 @@ class CoarseProcessor extends AudioWorkletProcessor { } process(inputs, outputs, parameters) { + const input = inputs[0]; + const output = outputs[0]; + const blockSize = 128; + let coarse = parameters.coarse[0] ?? 0; coarse = Math.max(1, coarse); - return processSample(inputs, outputs, (block, n, inChannel, outChannel) => { - const value = n % coarse === 0 ? block : outChannel[n - 1]; - return value; - }); + + 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 true; } } - registerProcessor('coarse-processor', CoarseProcessor); class CrushProcessor extends AudioWorkletProcessor { @@ -49,13 +41,23 @@ class CrushProcessor extends AudioWorkletProcessor { } process(inputs, outputs, parameters) { + const input = inputs[0]; + const output = outputs[0]; + const blockSize = 128; + let crush = parameters.crush[0] ?? 8; crush = Math.max(1, crush); - return processSample(inputs, outputs, (block) => { - const x = Math.pow(2, crush - 1); - return Math.round(block * x) / x; - }); + 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 true; } } registerProcessor('crush-processor', CrushProcessor); @@ -73,17 +75,26 @@ class ShapeProcessor extends AudioWorkletProcessor { } process(inputs, outputs, parameters) { + const input = inputs[0]; + const output = outputs[0]; + const blockSize = 128; + let shape = parameters.shape[0]; - const postgain = Math.max(0.001, Math.min(1, parameters.postgain[0])); shape = shape < 1 ? shape : 1.0 - 4e-10; shape = (2.0 * shape) / (1.0 - shape); - return processSample(inputs, outputs, (block) => { - const val = ((1 + shape) * block) / (1 + shape * Math.abs(block)); - return val * postgain; - }); + 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('shape-processor', ShapeProcessor); class DistortProcessor extends AudioWorkletProcessor { @@ -99,14 +110,22 @@ class DistortProcessor extends AudioWorkletProcessor { } process(inputs, outputs, parameters) { - let shape = parameters.distort[0]; + 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])); - shape = Math.expm1(shape); - return processSample(inputs, outputs, (block) => { - const val = ((1 + shape) * block) / (1 + shape * Math.abs(block)); - return val * postgain; - }); + + 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); From d5ed0650111dd8d6bc6c1d3cf0e44f0d248def6e Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 3 Mar 2024 22:50:51 +0100 Subject: [PATCH 19/24] fix: hydra canvas adjust to screen size + simplify hydra init (use getDrawContext) + make getDrawContext more versatile --- packages/canvas/draw.mjs | 15 ++++++++------- packages/hydra/hydra.mjs | 21 +++++++++------------ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/packages/canvas/draw.mjs b/packages/canvas/draw.mjs index b03634c5..20bf7fd6 100644 --- a/packages/canvas/draw.mjs +++ b/packages/canvas/draw.mjs @@ -6,26 +6,27 @@ This program is free software: you can redistribute it and/or modify it under th 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 = window.devicePixelRatio || 1; 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 } = {}) { diff --git a/packages/hydra/hydra.mjs b/packages/hydra/hydra.mjs index b7912446..17830d81 100644 --- a/packages/hydra/hydra.mjs +++ b/packages/hydra/hydra.mjs @@ -2,15 +2,6 @@ import { getDrawContext } from '@strudel/canvas'; 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; -} - export async function initHydra(options = {}) { // reset if options have changed since last init if (latestOptions && JSON.stringify(latestOptions) !== JSON.stringify(options)) { @@ -19,12 +10,19 @@ 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); @@ -33,7 +31,6 @@ export async function initHydra(options = {}) { canvas.style.display = 'none'; hydra.synth.s0.init({ src: canvas }); } - appendCanvas(hydra); } } From 8e41332dfa0ce0d99414d8b712103ef517605a88 Mon Sep 17 00:00:00 2001 From: Alex McLean Date: Wed, 6 Mar 2024 11:14:48 +0000 Subject: [PATCH 20/24] alias - for ~ (#981) --- packages/mini/mini.mjs | 2 +- packages/mini/test/mini.test.mjs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) 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?') From 75098adbdbe1a83b077f52967bc4168de1becfc7 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 7 Mar 2024 13:17:40 +0100 Subject: [PATCH 21/24] allow offsetting onFrame --- packages/canvas/draw.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/canvas/draw.mjs b/packages/canvas/draw.mjs index 20bf7fd6..2ea531cf 100644 --- a/packages/canvas/draw.mjs +++ b/packages/canvas/draw.mjs @@ -64,7 +64,7 @@ Pattern.prototype.draw = function (callback, { from, to, onQuery } = {}) { // 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) { +Pattern.prototype.onFrame = function (fn, offset = 0) { if (typeof window === 'undefined') { return this; } @@ -72,7 +72,7 @@ Pattern.prototype.onFrame = function (fn) { cancelAnimationFrame(window.strudelAnimation); } const animate = () => { - const t = getTime(); + const t = getTime() + offset; const haps = this.queryArc(t, t); fn(haps, t, this); window.strudelAnimation = requestAnimationFrame(animate); From 0f07bfc9d758ba6c2083d7d7ea75882935fc98a1 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 7 Mar 2024 13:24:52 +0100 Subject: [PATCH 22/24] rename: canvas -> draw --- packages/codemirror/codemirror.mjs | 2 +- packages/codemirror/package.json | 2 +- packages/{canvas => draw}/README.md | 0 packages/{canvas => draw}/animate.mjs | 0 packages/{canvas => draw}/color.mjs | 0 packages/{canvas => draw}/draw.mjs | 0 packages/{canvas => draw}/index.mjs | 0 packages/{canvas => draw}/package.json | 4 +- packages/{canvas => draw}/pianoroll.mjs | 0 packages/{canvas => draw}/spiral.mjs | 0 packages/{canvas => draw}/vite.config.js | 0 packages/hydra/hydra.mjs | 2 +- packages/hydra/package.json | 2 +- packages/repl/package.json | 2 +- packages/repl/prebake.mjs | 2 +- packages/repl/repl-component.mjs | 2 +- packages/webaudio/package.json | 2 +- packages/webaudio/scope.mjs | 2 +- pnpm-lock.yaml | 50 ++++++++++----------- website/package.json | 2 +- website/src/docs/Colors.jsx | 2 +- website/src/docs/MiniRepl.jsx | 2 +- website/src/pages/img/example-[name].png.js | 2 +- website/src/pages/swatch/[name].png.js | 2 +- website/src/repl/Repl.jsx | 2 +- website/src/repl/util.mjs | 2 +- 26 files changed, 43 insertions(+), 43 deletions(-) rename packages/{canvas => draw}/README.md (100%) rename packages/{canvas => draw}/animate.mjs (100%) rename packages/{canvas => draw}/color.mjs (100%) rename packages/{canvas => draw}/draw.mjs (100%) rename packages/{canvas => draw}/index.mjs (100%) rename packages/{canvas => draw}/package.json (88%) rename packages/{canvas => draw}/pianoroll.mjs (100%) rename packages/{canvas => draw}/spiral.mjs (100%) rename packages/{canvas => draw}/vite.config.js (100%) diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index 3209ef87..512c873a 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -13,7 +13,7 @@ import { drawSelection, } from '@codemirror/view'; import { Pattern, repl } from '@strudel/core'; -import { Drawer, cleanupDraw } from '@strudel/canvas'; +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 79d2e022..0b39ebdd 100644 --- a/packages/codemirror/package.json +++ b/packages/codemirror/package.json @@ -45,7 +45,7 @@ "@replit/codemirror-vim": "^6.1.0", "@replit/codemirror-vscode-keymap": "^6.0.2", "@strudel/core": "workspace:*", - "@strudel/canvas": "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/canvas/README.md b/packages/draw/README.md similarity index 100% rename from packages/canvas/README.md rename to packages/draw/README.md diff --git a/packages/canvas/animate.mjs b/packages/draw/animate.mjs similarity index 100% rename from packages/canvas/animate.mjs rename to packages/draw/animate.mjs diff --git a/packages/canvas/color.mjs b/packages/draw/color.mjs similarity index 100% rename from packages/canvas/color.mjs rename to packages/draw/color.mjs diff --git a/packages/canvas/draw.mjs b/packages/draw/draw.mjs similarity index 100% rename from packages/canvas/draw.mjs rename to packages/draw/draw.mjs diff --git a/packages/canvas/index.mjs b/packages/draw/index.mjs similarity index 100% rename from packages/canvas/index.mjs rename to packages/draw/index.mjs diff --git a/packages/canvas/package.json b/packages/draw/package.json similarity index 88% rename from packages/canvas/package.json rename to packages/draw/package.json index f87a7e19..346a4723 100644 --- a/packages/canvas/package.json +++ b/packages/draw/package.json @@ -1,7 +1,7 @@ { - "name": "@strudel/canvas", + "name": "@strudel/draw", "version": "1.0.1", - "description": "Helpers for drawing with the Canvas API and Strudel", + "description": "Helpers for drawing with Strudel", "main": "index.mjs", "type": "module", "publishConfig": { diff --git a/packages/canvas/pianoroll.mjs b/packages/draw/pianoroll.mjs similarity index 100% rename from packages/canvas/pianoroll.mjs rename to packages/draw/pianoroll.mjs diff --git a/packages/canvas/spiral.mjs b/packages/draw/spiral.mjs similarity index 100% rename from packages/canvas/spiral.mjs rename to packages/draw/spiral.mjs diff --git a/packages/canvas/vite.config.js b/packages/draw/vite.config.js similarity index 100% rename from packages/canvas/vite.config.js rename to packages/draw/vite.config.js diff --git a/packages/hydra/hydra.mjs b/packages/hydra/hydra.mjs index 17830d81..e784228d 100644 --- a/packages/hydra/hydra.mjs +++ b/packages/hydra/hydra.mjs @@ -1,4 +1,4 @@ -import { getDrawContext } from '@strudel/canvas'; +import { getDrawContext } from '@strudel/draw'; let latestOptions; diff --git a/packages/hydra/package.json b/packages/hydra/package.json index c635314c..77fab126 100644 --- a/packages/hydra/package.json +++ b/packages/hydra/package.json @@ -34,7 +34,7 @@ "homepage": "https://github.com/tidalcycles/strudel#readme", "dependencies": { "@strudel/core": "workspace:*", - "@strudel/canvas": "workspace:*", + "@strudel/draw": "workspace:*", "hydra-synth": "^1.3.29" }, "devDependencies": { diff --git a/packages/repl/package.json b/packages/repl/package.json index 742c5bb7..70687d1c 100644 --- a/packages/repl/package.json +++ b/packages/repl/package.json @@ -35,7 +35,7 @@ "dependencies": { "@strudel/codemirror": "workspace:*", "@strudel/core": "workspace:*", - "@strudel/canvas": "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 1858580b..9fc1c881 100644 --- a/packages/repl/prebake.mjs +++ b/packages/repl/prebake.mjs @@ -6,7 +6,7 @@ export async function prebake() { const modulesLoading = evalScope( // import('@strudel/core'), core, - import('@strudel/canvas'), + 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 4bdd6fe6..4fa8d6d2 100644 --- a/packages/repl/repl-component.mjs +++ b/packages/repl/repl-component.mjs @@ -1,5 +1,5 @@ import { silence } from '@strudel/core'; -import { getDrawContext } from '@strudel/canvas'; +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/webaudio/package.json b/packages/webaudio/package.json index 29348f48..f9cd7cc7 100644 --- a/packages/webaudio/package.json +++ b/packages/webaudio/package.json @@ -34,7 +34,7 @@ "homepage": "https://github.com/tidalcycles/strudel#readme", "dependencies": { "@strudel/core": "workspace:*", - "@strudel/canvas": "workspace:*", + "@strudel/draw": "workspace:*", "superdough": "workspace:*" }, "devDependencies": { diff --git a/packages/webaudio/scope.mjs b/packages/webaudio/scope.mjs index 288880c0..0371366c 100644 --- a/packages/webaudio/scope.mjs +++ b/packages/webaudio/scope.mjs @@ -1,5 +1,5 @@ import { Pattern, clamp } from '@strudel/core'; -import { getDrawContext } from '@strudel/canvas'; +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 1ef01efc..b123e2b7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -140,16 +140,6 @@ importers: specifier: ^5.0.10 version: 5.0.10 - packages/canvas: - dependencies: - '@strudel/core': - specifier: workspace:* - version: link:../core - devDependencies: - vite: - specifier: ^5.0.10 - version: 5.0.11(@types/node@20.10.6) - packages/codemirror: dependencies: '@codemirror/autocomplete': @@ -188,12 +178,12 @@ importers: '@replit/codemirror-vscode-keymap': specifier: ^6.0.2 version: 6.0.2(@codemirror/autocomplete@6.11.1)(@codemirror/commands@6.3.3)(@codemirror/language@6.10.0)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.5)(@codemirror/state@6.4.0)(@codemirror/view@6.23.0) - '@strudel/canvas': - specifier: workspace:* - version: link:../canvas '@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) @@ -246,16 +236,26 @@ 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: dependencies: - '@strudel/canvas': - specifier: workspace:* - version: link:../canvas '@strudel/core': specifier: workspace:* version: link:../core + '@strudel/draw': + specifier: workspace:* + version: link:../draw hydra-synth: specifier: ^1.3.29 version: 1.3.29 @@ -317,15 +317,15 @@ importers: packages/repl: dependencies: - '@strudel/canvas': - specifier: workspace:* - version: link:../canvas '@strudel/codemirror': specifier: workspace:* version: link:../codemirror '@strudel/core': specifier: workspace:* version: link:../core + '@strudel/draw': + specifier: workspace:* + version: link:../draw '@strudel/hydra': specifier: workspace:* version: link:../hydra @@ -474,12 +474,12 @@ importers: packages/webaudio: dependencies: - '@strudel/canvas': - specifier: workspace:* - version: link:../canvas '@strudel/core': specifier: workspace:* version: link:../core + '@strudel/draw': + specifier: workspace:* + version: link:../draw superdough: specifier: workspace:* version: link:../superdough @@ -545,9 +545,6 @@ importers: '@nanostores/react': specifier: ^0.7.1 version: 0.7.1(nanostores@0.9.5)(react@18.2.0) - '@strudel/canvas': - specifier: workspace:* - version: link:../packages/canvas '@strudel/codemirror': specifier: workspace:* version: link:../packages/codemirror @@ -560,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 diff --git a/website/package.json b/website/package.json index ed40b005..7af9e61e 100644 --- a/website/package.json +++ b/website/package.json @@ -26,7 +26,7 @@ "@nanostores/react": "^0.7.1", "@strudel/codemirror": "workspace:*", "@strudel/core": "workspace:*", - "@strudel/canvas": "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 f11a7440..34cc2e49 100644 --- a/website/src/docs/Colors.jsx +++ b/website/src/docs/Colors.jsx @@ -1,4 +1,4 @@ -import { colorMap } from '@strudel/canvas'; +import { colorMap } from '@strudel/draw'; const Colors = () => { return ( diff --git a/website/src/docs/MiniRepl.jsx b/website/src/docs/MiniRepl.jsx index 9a886109..cb0f26f7 100644 --- a/website/src/docs/MiniRepl.jsx +++ b/website/src/docs/MiniRepl.jsx @@ -1,7 +1,7 @@ import { useState, useRef, useCallback, useMemo, useEffect } from 'react'; import { Icon } from './Icon'; import { silence, noteToMidi, _mod } from '@strudel/core'; -import { getPunchcardPainter } from '@strudel/canvas'; +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 1361df79..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/canvas'; +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/swatch/[name].png.js b/website/src/pages/swatch/[name].png.js index 809a64ec..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/canvas'; +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 fd5892bd..dd164965 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th */ import { code2hash, logger, silence } from '@strudel/core'; -import { getDrawContext } from '@strudel/canvas'; +import { getDrawContext } from '@strudel/draw'; import cx from '@src/cx.mjs'; import { transpiler } from '@strudel/transpiler'; import { diff --git a/website/src/repl/util.mjs b/website/src/repl/util.mjs index d9c80ca9..6dba7dab 100644 --- a/website/src/repl/util.mjs +++ b/website/src/repl/util.mjs @@ -72,7 +72,7 @@ export async function getRandomTune() { export function loadModules() { let modules = [ import('@strudel/core'), - import('@strudel/canvas'), + import('@strudel/draw'), import('@strudel/tonal'), import('@strudel/mini'), import('@strudel/xen'), From f1700d5e4705dba60ccca0e0abcc286d9733cc0d Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Thu, 7 Mar 2024 23:35:04 -0500 Subject: [PATCH 23/24] fixed --- packages/superdough/README.md | 2 +- website/src/pages/learn/effects.mdx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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/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 From 012b19c79ea80c810c4d788ee43dc63977a8f0cb Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 10 Mar 2024 00:29:52 +0100 Subject: [PATCH 24/24] fix: clear hydra on reset --- packages/hydra/hydra.mjs | 16 +++++++++++++--- website/src/repl/Repl.jsx | 8 ++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/hydra/hydra.mjs b/packages/hydra/hydra.mjs index dc1ba3f9..83d25cc4 100644 --- a/packages/hydra/hydra.mjs +++ b/packages/hydra/hydra.mjs @@ -1,4 +1,4 @@ -import { getDrawContext } from '@strudel/core'; +import { getDrawContext, controls } from '@strudel/core'; let latestOptions; @@ -11,6 +11,7 @@ function appendCanvas(c) { return testCanvas; } +let hydra; export async function initHydra(options = {}) { // reset if options have changed since last init if (latestOptions && JSON.stringify(latestOptions) !== JSON.stringify(options)) { @@ -19,7 +20,6 @@ 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, @@ -27,7 +27,7 @@ export async function initHydra(options = {}) { } = { detectAudio: false, ...options }; await import(/* @vite-ignore */ src); - const hydra = new Hydra(hydraConfig); + hydra = new Hydra(hydraConfig); if (feedStrudel) { const { canvas } = getDrawContext(); canvas.style.display = 'none'; @@ -37,4 +37,14 @@ export async function initHydra(options = {}) { } } +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/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index c5a8fa09..e80b37a9 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -17,6 +17,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 +76,11 @@ export function Repl({ embedded = false }) { onUpdateState: (state) => { setReplState({ ...state }); }, + onToggle: (playing) => { + if (!playing) { + clearHydra(); + } + }, afterEval: (all) => { const { code } = all; setLatestCode(code); @@ -165,6 +171,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 +196,7 @@ export function Repl({ embedded = false }) { setActivePattern(patternData.id); setViewingPatternData(patternData); clearCanvas(); + clearHydra(); resetLoadedSounds(); resetGlobalEffects(); await prebake(); // declare default samples