- clip now works like legato in tidal

- supports floats
- hap.duration now respects clip value
- hap.endClipped is now end*clip
- visualizations show clipped length
- clip(0) will now be silence
This commit is contained in:
Felix Roos 2023-06-12 22:39:32 +02:00
parent e4a35fdd72
commit 8c9e06c329
5 changed files with 11 additions and 7 deletions

View File

@ -133,7 +133,7 @@ export class StrudelMirror {
this.code = initialCode;
this.drawer = new Drawer((haps, time) => {
const currentFrame = haps.filter((hap) => time >= hap.whole.begin && time <= hap.whole.end);
const currentFrame = haps.filter((hap) => time >= hap.whole.begin && time <= hap.endClipped);
this.highlight(currentFrame);
onDraw?.(haps, time, currentFrame);
}, drawTime);

View File

@ -32,7 +32,11 @@ export class Hap {
}
get duration() {
return this.whole.end.sub(this.whole.begin);
return this.whole.end.sub(this.whole.begin).mul(typeof this.value?.clip === 'number' ? this.value?.clip : 1);
}
get endClipped() {
return this.whole.begin.add(this.duration);
}
wholeOrPart() {

View File

@ -83,9 +83,9 @@ Pattern.prototype.pianoroll = function ({
ctx.fillRect(0, 0, w, h);
}
const inFrame = (event) =>
(!hideNegative || event.whole.begin >= 0) && event.whole.begin <= t + to && event.whole.end >= t + from;
(!hideNegative || event.whole.begin >= 0) && event.whole.begin <= t + to && event.endClipped >= t + from;
events.filter(inFrame).forEach((event) => {
const isActive = event.whole.begin <= t && event.whole.end > t;
const isActive = event.whole.begin <= t && event.endClipped > t;
ctx.fillStyle = event.context?.color || inactive;
ctx.strokeStyle = event.context?.color || active;
ctx.globalAlpha = event.context.velocity ?? event.value?.gain ?? 1;

View File

@ -17,7 +17,7 @@ function useHighlighting({ view, pattern, active, getTime }) {
const begin = Math.max(lastEnd.current ?? audioTime, audioTime - 1 / 10, -0.01); // negative time seems buggy
const span = [round(begin), round(audioTime + 1 / 60)];
lastEnd.current = span[1];
highlights.current = highlights.current.filter((hap) => hap.whole.end > audioTime); // keep only highlights that are still active
highlights.current = highlights.current.filter((hap) => hap.endClipped > audioTime); // keep only highlights that are still active
const haps = pattern.queryArc(...span).filter((hap) => hap.hasOnset());
highlights.current = highlights.current.concat(haps); // add potential new onsets
view.dispatch({ effects: setHighlights.of({ haps: highlights.current }) }); // highlight all still active + new active haps

View File

@ -170,7 +170,7 @@ export async function onTriggerSample(t, value, onended, bank) {
nudge = 0, // TODO: is this in seconds?
cut,
loop,
clip = 0, // if 1, samples will be cut off when the hap ends
clip = undefined, // if 1, samples will be cut off when the hap ends
n = 0,
note,
speed = 1, // sample playback speed
@ -232,7 +232,7 @@ export async function onTriggerSample(t, value, onended, bank) {
out.disconnect();
onended();
};
const stop = (endTime, playWholeBuffer = !clip) => {
const stop = (endTime, playWholeBuffer = [undefined, false, null].includes(clip)) => {
let releaseTime = endTime;
if (playWholeBuffer) {
releaseTime = t + (end - begin) * bufferDuration;