From 5c71bb95a0ec1384b45a0716d9fb2963036dea3d Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 28 Feb 2024 18:51:07 +0100 Subject: [PATCH 1/5] remove legacy legato + make legato a synonym of clip --- packages/core/controls.mjs | 18 ++---------------- packages/core/pattern.mjs | 15 --------------- website/src/pages/learn/time-modifiers.mdx | 6 +----- 3 files changed, 3 insertions(+), 36 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 42fa8cba..b6964564 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -400,19 +400,6 @@ export const { loopEnd, loope } = registerControl('loopEnd', 'loope'); * 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'); /** @@ -1455,16 +1442,15 @@ 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 + * @synonyms legato * @param {number | Pattern} factor >= 0 * @example * note("c a f e").s("piano").clip("<.5 1 2>") * */ -export const { clip } = registerControl('clip'); +export const { clip, legato } = registerControl('clip', 'legato'); // ZZFX export const { zrand } = registerControl('zrand'); diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 3d859728..6efcc758 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -2241,21 +2241,6 @@ export const velocity = register('velocity', function (velocity, pat) { return pat.withContext((context) => ({ ...context, velocity: (context.velocity || 1) * velocity })); }); -/** - * - * Multiplies the hap duration with the given factor. - * With samples, `clip` might be a better function to use ([more info](https://github.com/tidalcycles/strudel/pull/598)) - * @name legato - * @memberof Pattern - * @example - * note("c3 eb3 g3 c4").legato("<.25 .5 1 2>") - */ -// TODO - fix -export const legato = register('legato', function (value, pat) { - value = Fraction(value); - return pat.withHapSpan((span) => new TimeSpan(span.begin, span.begin.add(span.end.sub(span.begin).mul(value)))); -}); - ////////////////////////////////////////////////////////////////////// // Control-related functions, i.e. ones that manipulate patterns of // objects diff --git a/website/src/pages/learn/time-modifiers.mdx b/website/src/pages/learn/time-modifiers.mdx index e1226512..1baef9ea 100644 --- a/website/src/pages/learn/time-modifiers.mdx +++ b/website/src/pages/learn/time-modifiers.mdx @@ -34,11 +34,7 @@ Some of these have equivalent operators in the Mini Notation: -## legato - - - -## clip +## clip / legato From f779e81993668b56308b4e125a9c82f436ef4c3b Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 28 Feb 2024 18:52:08 +0100 Subject: [PATCH 2/5] remove old snapshots --- test/__snapshots__/examples.test.mjs.snap | 42 ----------------------- 1 file changed, 42 deletions(-) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 0c28f04b..09aec7b2 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -3521,48 +3521,6 @@ exports[`runs examples > example "layer" example index 0 1`] = ` ] `; -exports[`runs examples > example "legato" example index 0 1`] = ` -[ - "[ 0/1 → 1/32 | c4 ]", - "[ 1/4 → 9/32 | eb4 ]", - "[ 1/2 → 17/32 | g4 ]", - "[ 3/4 → 25/32 | bb4 ]", - "[ 1/1 → 17/16 | c4 ]", - "[ 5/4 → 21/16 | eb4 ]", - "[ 3/2 → 25/16 | g4 ]", - "[ 7/4 → 29/16 | bb4 ]", - "[ 2/1 → 17/8 | c4 ]", - "[ 9/4 → 19/8 | eb4 ]", - "[ 5/2 → 21/8 | g4 ]", - "[ 11/4 → 23/8 | bb4 ]", - "[ 3/1 → 51/16 | c4 ]", - "[ 13/4 → 55/16 | eb4 ]", - "[ 7/2 → 59/16 | g4 ]", - "[ 15/4 → 63/16 | bb4 ]", -] -`; - -exports[`runs examples > example "legato" example index 0 2`] = ` -[ - "[ 0/1 → 1/16 | note:c3 ]", - "[ 1/4 → 5/16 | note:eb3 ]", - "[ 1/2 → 9/16 | note:g3 ]", - "[ 3/4 → 13/16 | note:c4 ]", - "[ 1/1 → 9/8 | note:c3 ]", - "[ 5/4 → 11/8 | note:eb3 ]", - "[ 3/2 → 13/8 | note:g3 ]", - "[ 7/4 → 15/8 | note:c4 ]", - "[ 2/1 → 9/4 | note:c3 ]", - "[ 9/4 → 5/2 | note:eb3 ]", - "[ 5/2 → 11/4 | note:g3 ]", - "[ 11/4 → 3/1 | note:c4 ]", - "[ 3/1 → 7/2 | note:c3 ]", - "[ 13/4 → 15/4 | note:eb3 ]", - "[ 7/2 → 4/1 | note:g3 ]", - "[ 15/4 → 17/4 | note:c4 ]", -] -`; - exports[`runs examples > example "leslie" example index 0 1`] = ` [ "[ 0/1 → 1/1 | n:0 s:supersquare leslie:0 ]", From eb93a6c1495df43a01ad9c943dbf58a64a71b1a8 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 29 Feb 2024 04:05:05 +0100 Subject: [PATCH 3/5] refactor: duration is now a regular control --- packages/core/controls.mjs | 14 ++++++++++++-- packages/core/draw.mjs | 2 +- packages/core/hap.mjs | 12 +++++++++++- packages/core/pattern.mjs | 6 ------ packages/superdough/sampler.mjs | 4 ++-- packages/webaudio/webaudio.mjs | 1 - 6 files changed, 26 insertions(+), 13 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index b6964564..671566be 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1376,8 +1376,6 @@ export const { waveloss } = registerControl('waveloss'); * */ export const { density } = registerControl('density'); -// TODO: midi effects? -export const { dur } = registerControl('dur'); // ['modwheel'], export const { expression } = registerControl('expression'); export const { sustainpedal } = registerControl('sustainpedal'); @@ -1452,6 +1450,18 @@ export const { cps } = registerControl('cps'); */ export const { clip, legato } = registerControl('clip', 'legato'); +/** + * Sets the duration of the event in cycles. Similar to clip / legato, it also cuts samples off at the end if they exceed the duration. + * + * @name duration + * @synonyms dur + * @param {number | Pattern} seconds >= 0 + * @example + * note("c a f e").s("piano").dur("<.5 1 2>") + * + */ +export const { duration, dur } = registerControl('duration', 'dur'); + // ZZFX export const { zrand } = registerControl('zrand'); export const { curve } = registerControl('curve'); diff --git a/packages/core/draw.mjs b/packages/core/draw.mjs index 7a9454f2..30de7ba8 100644 --- a/packages/core/draw.mjs +++ b/packages/core/draw.mjs @@ -134,7 +134,7 @@ export class Drawer { this.lastFrame = phase; this.visibleHaps = (this.visibleHaps || []) // filter out haps that are too far in the past (think left edge of screen for pianoroll) - .filter((h) => h.whole?.end >= phase - lookbehind - lookahead) + .filter((h) => h.endClipped >= phase - lookbehind - lookahead) // add new haps with onset (think right edge bars scrolling in) .concat(haps.filter((h) => h.hasOnset())); const time = phase - lookahead; diff --git a/packages/core/hap.mjs b/packages/core/hap.mjs index 6a1eb987..ab5f0c97 100644 --- a/packages/core/hap.mjs +++ b/packages/core/hap.mjs @@ -3,6 +3,7 @@ hap.mjs - 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 Fraction from './fraction.mjs'; export class Hap { /* @@ -32,7 +33,16 @@ export class Hap { } get duration() { - return this.whole.end.sub(this.whole.begin).mul(typeof this.value?.clip === 'number' ? this.value?.clip : 1); + let duration; + if (typeof this.value?.duration === 'number') { + duration = Fraction(this.value.duration); + } else { + duration = this.whole.end.sub(this.whole.begin); + } + if (typeof this.value?.clip === 'number') { + return duration.mul(this.value.clip); + } + return duration; } get endClipped() { diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 6efcc758..13ac8cfd 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -2203,12 +2203,6 @@ export const bypass = register('bypass', function (on, pat) { */ export const ribbon = register('ribbon', (offset, cycles, pat) => pat.early(offset).restart(pure(1).slow(cycles))); -// sets absolute duration of haps -// TODO - fix -export const duration = register('duration', function (value, pat) { - return pat.withHapSpan((span) => new TimeSpan(span.begin, span.begin.add(value))); -}); - export const hsla = register('hsla', (h, s, l, a, pat) => { return pat.color(`hsla(${h}turn,${s * 100}%,${l * 100}%,${a})`); }); diff --git a/packages/superdough/sampler.mjs b/packages/superdough/sampler.mjs index e7d1dae3..437347ca 100644 --- a/packages/superdough/sampler.mjs +++ b/packages/superdough/sampler.mjs @@ -251,7 +251,7 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) { nudge = 0, // TODO: is this in seconds? cut, loop, - clip = undefined, // if 1, samples will be cut off when the hap ends + clip = undefined, // if set, samples will be cut off when the hap ends n = 0, note, speed = 1, // sample playback speed @@ -306,7 +306,7 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) { bufferSource.start(time, offset); const envGain = ac.createGain(); const node = bufferSource.connect(envGain); - if (clip == null && loop == null && value.release == null) { + if (duration == null && clip == null && loop == null && value.release == null) { const bufferDuration = bufferSource.buffer.duration / bufferSource.playbackRate.value; duration = (end - begin) * bufferDuration; } diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 19dbb804..a176d2cc 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -15,7 +15,6 @@ const hap2value = (hap) => { return { ...hap.value, velocity: hap.context.velocity }; }; -// TODO: bind logger export const webaudioOutputTrigger = (t, hap, ct, cps) => superdough(hap2value(hap), t - ct, hap.duration / cps, cps); export const webaudioOutput = (hap, deadline, hapDuration) => superdough(hap2value(hap), deadline, hapDuration); From 7556da783989d94f413e833c29377651b2f04b6a Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 29 Feb 2024 04:06:15 +0100 Subject: [PATCH 4/5] snapshot --- test/__snapshots__/examples.test.mjs.snap | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 09aec7b2..dc6da174 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -2001,6 +2001,27 @@ exports[`runs examples > example "dry" example index 0 1`] = ` ] `; +exports[`runs examples > example "duration" example index 0 1`] = ` +[ + "[ 0/1 → 1/4 | note:c s:piano duration:0.5 ]", + "[ 1/4 → 1/2 | note:a s:piano duration:0.5 ]", + "[ 1/2 → 3/4 | note:f s:piano duration:0.5 ]", + "[ 3/4 → 1/1 | note:e s:piano duration:0.5 ]", + "[ 1/1 → 5/4 | note:c s:piano duration:1 ]", + "[ 5/4 → 3/2 | note:a s:piano duration:1 ]", + "[ 3/2 → 7/4 | note:f s:piano duration:1 ]", + "[ 7/4 → 2/1 | note:e s:piano duration:1 ]", + "[ 2/1 → 9/4 | note:c s:piano duration:2 ]", + "[ 9/4 → 5/2 | note:a s:piano duration:2 ]", + "[ 5/2 → 11/4 | note:f s:piano duration:2 ]", + "[ 11/4 → 3/1 | note:e s:piano duration:2 ]", + "[ 3/1 → 13/4 | note:c s:piano duration:0.5 ]", + "[ 13/4 → 7/2 | note:a s:piano duration:0.5 ]", + "[ 7/2 → 15/4 | note:f s:piano duration:0.5 ]", + "[ 15/4 → 4/1 | note:e s:piano duration:0.5 ]", +] +`; + exports[`runs examples > example "early" example index 0 1`] = ` [ "[ -1/10 ⇜ (0/1 → 2/5) | s:hh ]", From 96eef75f74c355a95d8cba2b420271ddf3794a85 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 29 Feb 2024 10:33:27 +0100 Subject: [PATCH 5/5] fix: end / begin. sampler now needs clip to choose duration... --- packages/superdough/sampler.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/superdough/sampler.mjs b/packages/superdough/sampler.mjs index 437347ca..85fccc97 100644 --- a/packages/superdough/sampler.mjs +++ b/packages/superdough/sampler.mjs @@ -306,7 +306,7 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) { bufferSource.start(time, offset); const envGain = ac.createGain(); const node = bufferSource.connect(envGain); - if (duration == null && clip == null && loop == null && value.release == null) { + if (clip == null && loop == null && value.release == null) { const bufferDuration = bufferSource.buffer.duration / bufferSource.playbackRate.value; duration = (end - begin) * bufferDuration; }