From eb93a6c1495df43a01ad9c943dbf58a64a71b1a8 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 29 Feb 2024 04:05:05 +0100 Subject: [PATCH] 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);