From 1a0dff40817bfed6fb38fa22e3b485d6f37b4878 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Mar 2024 05:47:50 +0100 Subject: [PATCH 01/10] add some hap methods for time introspection --- packages/core/hap.mjs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/core/hap.mjs b/packages/core/hap.mjs index ab5f0c97..7f35ce55 100644 --- a/packages/core/hap.mjs +++ b/packages/core/hap.mjs @@ -49,6 +49,24 @@ export class Hap { return this.whole.begin.add(this.duration); } + isActive(currentTime) { + return this.whole.begin <= currentTime && this.endClipped > currentTime; + } + + isInPast(currentTime) { + return currentTime > this.endClipped; + } + isInNearPast(margin, currentTime) { + return currentTime - margin <= this.endClipped; + } + + isInFuture(currentTime) { + return currentTime < this.whole.begin; + } + isInNearFuture(margin, currentTime) { + return currentTime < this.whole.begin && currentTime > this.whole.begin - margin; + } + wholeOrPart() { return this.whole ? this.whole : this.part; } From 04a0952a223a8fabe77461c8b57ce65351cc9f61 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Mar 2024 06:25:24 +0100 Subject: [PATCH 02/10] simplify draw plumbing --- packages/codemirror/codemirror.mjs | 13 +++++++++---- packages/core/hap.mjs | 2 +- packages/draw/pianoroll.mjs | 4 ++-- packages/repl/repl-component.mjs | 11 +---------- website/src/docs/MiniRepl.jsx | 12 +----------- website/src/repl/Repl.jsx | 9 +-------- 6 files changed, 15 insertions(+), 36 deletions(-) diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index e59249ee..5602a052 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -128,6 +128,7 @@ export class StrudelMirror { id, initialCode = '', onDraw, + drawContext, drawTime = [0, 0], autodraw, prebake, @@ -140,13 +141,14 @@ export class StrudelMirror { this.widgets = []; this.painters = []; this.drawTime = drawTime; - this.onDraw = onDraw; + this.drawContext = drawContext; + this.onDraw = onDraw || this.draw; this.id = id || s4(); this.drawer = new Drawer((haps, time) => { - const currentFrame = haps.filter((hap) => time >= hap.whole.begin && time <= hap.endClipped); + const currentFrame = haps.filter((hap) => hap.isActive(time)); this.highlight(currentFrame, time); - this.onDraw?.(haps, time, currentFrame, this.painters); + this.onDraw(haps, time, this.painters); }, drawTime); this.prebaked = prebake(); @@ -236,6 +238,9 @@ export class StrudelMirror { // when no painters are set, [0,0] is enough (just highlighting) this.drawer.setDrawTime(this.painters.length ? this.drawTime : [0, 0]); } + draw(haps, time) { + this.painters?.forEach((painter) => painter(this.drawContext, time, haps, this.drawTime)); + } async drawFirstFrame() { if (!this.onDraw) { return; @@ -246,7 +251,7 @@ export class StrudelMirror { await this.repl.evaluate(this.code, false); this.drawer.invalidate(this.repl.scheduler, -0.001); // draw at -0.001 to avoid haps at 0 to be visualized as active - this.onDraw?.(this.drawer.visibleHaps, -0.001, [], this.painters); + this.onDraw?.(this.drawer.visibleHaps, -0.001, this.painters); } catch (err) { console.warn('first frame could not be painted'); } diff --git a/packages/core/hap.mjs b/packages/core/hap.mjs index 7f35ce55..5488a638 100644 --- a/packages/core/hap.mjs +++ b/packages/core/hap.mjs @@ -50,7 +50,7 @@ export class Hap { } isActive(currentTime) { - return this.whole.begin <= currentTime && this.endClipped > currentTime; + return this.whole.begin <= currentTime && this.endClipped >= currentTime; } isInPast(currentTime) { diff --git a/packages/draw/pianoroll.mjs b/packages/draw/pianoroll.mjs index f24f6114..310bfd70 100644 --- a/packages/draw/pianoroll.mjs +++ b/packages/draw/pianoroll.mjs @@ -277,8 +277,8 @@ export function getDrawOptions(drawTime, options = {}) { export const getPunchcardPainter = (options = {}) => - (ctx, time, haps, drawTime, paintOptions = {}) => - pianoroll({ ctx, time, haps, ...getDrawOptions(drawTime, { ...paintOptions, ...options }) }); + (ctx, time, haps, drawTime) => + pianoroll({ ctx, time, haps, ...getDrawOptions(drawTime, options) }); Pattern.prototype.punchcard = function (options) { return this.onPaint(getPunchcardPainter(options)); diff --git a/packages/repl/repl-component.mjs b/packages/repl/repl-component.mjs index 4fa8d6d2..e6e0ee0e 100644 --- a/packages/repl/repl-component.mjs +++ b/packages/repl/repl-component.mjs @@ -41,17 +41,8 @@ if (typeof HTMLElement !== 'undefined') { initialCode: '// LOADING', pattern: silence, drawTime, - onDraw: (haps, time, frame, painters) => { - painters.length && drawContext.clearRect(0, 0, drawContext.canvas.width * 2, drawContext.canvas.height * 2); - painters?.forEach((painter) => { - // ctx time haps drawTime paintOptions - painter(drawContext, time, haps, drawTime, { clear: false }); - }); - }, + drawContext, prebake, - afterEval: ({ code }) => { - // window.location.hash = '#' + code2hash(code); - }, onUpdateState: (state) => { const event = new CustomEvent('update', { detail: state, diff --git a/website/src/docs/MiniRepl.jsx b/website/src/docs/MiniRepl.jsx index fe618965..cadebcb3 100644 --- a/website/src/docs/MiniRepl.jsx +++ b/website/src/docs/MiniRepl.jsx @@ -39,16 +39,6 @@ export function MiniRepl({ const init = useCallback(({ code, shouldDraw }) => { const drawContext = shouldDraw ? document.querySelector('#' + canvasId)?.getContext('2d') : null; - let onDraw; - if (shouldDraw) { - onDraw = (haps, time, frame, painters) => { - painters.length && drawContext?.clearRect(0, 0, drawContext.canvas.width * 2, drawContext.canvas.height * 2); - painters?.forEach((painter) => { - // ctx time haps drawTime paintOptions - painter(drawContext, time, haps, drawTime, { clear: false }); - }); - }; - } const editor = new StrudelMirror({ id, @@ -60,7 +50,7 @@ export function MiniRepl({ initialCode: '// LOADING', pattern: silence, drawTime, - onDraw, + drawContext, editPattern: (pat, id) => { if (onTrigger) { pat = pat.onTrigger(onTrigger, false); diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index 786aad66..b5439846 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -65,13 +65,6 @@ export function Repl({ embedded = false }) { const init = useCallback(() => { const drawTime = [-2, 2]; const drawContext = getDrawContext(); - const onDraw = (haps, time, frame, painters) => { - painters.length && drawContext.clearRect(0, 0, drawContext.canvas.width * 2, drawContext.canvas.height * 2); - painters?.forEach((painter) => { - // ctx time haps drawTime paintOptions - painter(drawContext, time, haps, drawTime, { clear: false }); - }); - }; const editor = new StrudelMirror({ sync: true, defaultOutput: webaudioOutput, @@ -84,7 +77,7 @@ export function Repl({ embedded = false }) { initialCode: '// LOADING', pattern: silence, drawTime, - onDraw, + drawContext, prebake: async () => Promise.all([modulesLoading, presets]), onUpdateState: (state) => { setReplState({ ...state }); From 060d2cfe2bd1512ed0b2e8689add78bce8e47d10 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Mar 2024 06:50:13 +0100 Subject: [PATCH 03/10] another hap time introspection method --- packages/core/hap.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/core/hap.mjs b/packages/core/hap.mjs index 5488a638..17ac6ad0 100644 --- a/packages/core/hap.mjs +++ b/packages/core/hap.mjs @@ -66,6 +66,9 @@ export class Hap { isInNearFuture(margin, currentTime) { return currentTime < this.whole.begin && currentTime > this.whole.begin - margin; } + isWithinTime(min, max) { + return this.whole.begin <= max && this.endClipped >= min; + } wholeOrPart() { return this.whole ? this.whole : this.part; From 14b1a1203fcaf1d97fa84240f3810e1da70e9e9a Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Mar 2024 07:25:44 +0100 Subject: [PATCH 04/10] fix: cyclist.now() should return 0 when stopped --- packages/core/cyclist.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index b0727ee5..2251dcf6 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -67,6 +67,9 @@ export class Cyclist { ); } now() { + if (!this.started) { + return 0; + } const secondsSinceLastTick = this.getTime() - this.lastTick - this.clock.duration; return this.lastBegin + secondsSinceLastTick * this.cps; // + this.clock.minLatency; } From eb8ac291360f1be2dff0ca01dc5bb773d4c0c2bd Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Mar 2024 07:26:32 +0100 Subject: [PATCH 05/10] add memory to onFrame --- packages/draw/draw.mjs | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/packages/draw/draw.mjs b/packages/draw/draw.mjs index 0b5a1a4f..898b0a66 100644 --- a/packages/draw/draw.mjs +++ b/packages/draw/draw.mjs @@ -71,16 +71,33 @@ Pattern.prototype.draw = function (callback, { id = 'std', from, to, onQuery, ct }; // 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 (id, fn, offset = 0) { +// maybe this could also use Drawer internally? might need to support setting a custom pattern in Drawer +let memory = {}; +Pattern.prototype.onFrame = function (fn, options) { if (typeof window === 'undefined') { return this; } + let { id = 1, lookbehind = 0, lookahead = 0 } = options; + let __t = Math.max(getTime(), 0); stopAnimationFrame(id); + lookbehind = Math.abs(lookbehind); + // init memory, clear future haps of old pattern + memory[id] = (memory[id] || []).filter((h) => !h.isInFuture(__t)); + let newFuture = this.queryArc(__t, __t + lookahead).filter((h) => h.hasOnset()); + memory[id] = memory[id].concat(newFuture); + + let last; const animate = () => { - const t = getTime() + offset; - const haps = this.queryArc(t, t); - fn(haps, t, this); + const _t = getTime(); + const t = _t + lookahead; + // filter out haps that are too far in the past + memory[id] = memory[id].filter((h) => h.isInNearPast(lookbehind, _t)); + // begin where we left off in last frame, but max -0.1s (inactive tab throttles to 1fps) + let begin = Math.max(last || t, t - 1 / 10); + const haps = this.queryArc(begin, t).filter((h) => h.hasOnset()); + memory[id] = memory[id].concat(haps); + last = t; // makes sure no haps are missed + fn(memory[id], _t, t, this); animationFrames[id] = requestAnimationFrame(animate); }; requestAnimationFrame(animate); From ab015ff48a7dd593ab4dd4034cc9fadd8b5678ef Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Mar 2024 07:35:01 +0100 Subject: [PATCH 06/10] remove old .draw, rename .onFrame to .draw + breaking: from, to are now called lookbehind, lookahead + migrate .pianoroll --- packages/draw/draw.mjs | 34 +--------------------------------- packages/draw/pianoroll.mjs | 17 +++++++---------- 2 files changed, 8 insertions(+), 43 deletions(-) diff --git a/packages/draw/draw.mjs b/packages/draw/draw.mjs index 898b0a66..b23b5909 100644 --- a/packages/draw/draw.mjs +++ b/packages/draw/draw.mjs @@ -39,41 +39,9 @@ function stopAnimationFrame(id) { function stopAllAnimations() { Object.keys(animationFrames).forEach((id) => stopAnimationFrame(id)); } -Pattern.prototype.draw = function (callback, { id = 'std', from, to, onQuery, ctx } = {}) { - if (typeof window === 'undefined') { - return this; - } - stopAnimationFrame(id); - ctx = ctx || getDrawContext(); - let cycle, - events = []; - const animate = (time) => { - const t = getTime(); - if (from !== undefined && to !== undefined) { - const currentCycle = Math.floor(t); - if (cycle !== currentCycle) { - cycle = currentCycle; - const begin = currentCycle + from; - const end = currentCycle + to; - setTimeout(() => { - events = this.query(new State(new TimeSpan(begin, end))) - .filter(Boolean) - .filter((event) => event.part.begin.equals(event.whole.begin)); - onQuery?.(events); - }, 0); - } - } - callback(ctx, events, t, time); - animationFrames[id] = requestAnimationFrame(animate); - }; - requestAnimationFrame(animate); - return this; -}; -// this is a more generic helper to get a rendering callback for the currently active haps -// maybe this could also use Drawer internally? might need to support setting a custom pattern in Drawer let memory = {}; -Pattern.prototype.onFrame = function (fn, options) { +Pattern.prototype.draw = function (fn, options) { if (typeof window === 'undefined') { return this; } diff --git a/packages/draw/pianoroll.mjs b/packages/draw/pianoroll.mjs index 310bfd70..dc2faac4 100644 --- a/packages/draw/pianoroll.mjs +++ b/packages/draw/pianoroll.mjs @@ -36,26 +36,23 @@ const getValue = (e) => { }; Pattern.prototype.pianoroll = function (options = {}) { - let { cycles = 4, playhead = 0.5, overscan = 1, hideNegative = false, ctx, id } = options; + let { cycles = 4, playhead = 0.5, overscan = 0, hideNegative = false, ctx = getDrawContext(), id = 1 } = options; let from = -cycles * playhead; let to = cycles * (1 - playhead); - + const inFrame = (hap, t) => (!hideNegative || hap.whole.begin >= 0) && hap.isWithinTime(t + from, t + to); this.draw( - (ctx, haps, t) => { - const inFrame = (event) => - (!hideNegative || event.whole.begin >= 0) && event.whole.begin <= t + to && event.endClipped >= t + from; + (haps, time) => { pianoroll({ ...options, - time: t, + time, ctx, - haps: haps.filter(inFrame), + haps: haps.filter((hap) => inFrame(hap, time)), }); }, { - from: from - overscan, - to: to + overscan, - ctx, + lookbehind: from - overscan, + lookahead: to + overscan, id, }, ); From b5720355f9e59c269776b6e9a5210949ff1f75ae Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Mar 2024 09:31:12 +0100 Subject: [PATCH 07/10] fix: allow multiple visuals for the same pattern + using .tag function instead of single .id --- packages/codemirror/widget.mjs | 8 ++++---- packages/core/hap.mjs | 4 ++++ packages/core/pattern.mjs | 10 +++++----- packages/draw/pianoroll.mjs | 2 +- packages/draw/spiral.mjs | 2 +- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/codemirror/widget.mjs b/packages/codemirror/widget.mjs index 98d78b36..1d86dc35 100644 --- a/packages/codemirror/widget.mjs +++ b/packages/codemirror/widget.mjs @@ -106,23 +106,23 @@ function getCanvasWidget(id, options = {}) { registerWidget('_pianoroll', (id, options = {}, pat) => { const ctx = getCanvasWidget(id, options).getContext('2d'); - return pat.id(id).pianoroll({ fold: 1, ...options, ctx, id }); + return pat.tag(id).pianoroll({ fold: 1, ...options, ctx, id }); }); registerWidget('_punchcard', (id, options = {}, pat) => { const ctx = getCanvasWidget(id, options).getContext('2d'); - return pat.id(id).punchcard({ fold: 1, ...options, ctx, id }); + return pat.tag(id).punchcard({ fold: 1, ...options, ctx, id }); }); registerWidget('_spiral', (id, options = {}, pat) => { let _size = options.size || 275; options = { width: _size, height: _size, ...options, size: _size / 5 }; const ctx = getCanvasWidget(id, options).getContext('2d'); - return pat.id(id).spiral({ ...options, ctx, id }); + return pat.tag(id).spiral({ ...options, ctx, id }); }); registerWidget('_scope', (id, options = {}, pat) => { options = { width: 500, height: 60, pos: 0.5, scale: 1, ...options }; const ctx = getCanvasWidget(id, options).getContext('2d'); - return pat.id(id).scope({ ...options, ctx, id }); + return pat.tag(id).scope({ ...options, ctx, id }); }); diff --git a/packages/core/hap.mjs b/packages/core/hap.mjs index 17ac6ad0..7a9e0a62 100644 --- a/packages/core/hap.mjs +++ b/packages/core/hap.mjs @@ -91,6 +91,10 @@ export class Hap { return this.whole != undefined && this.whole.begin.equals(this.part.begin); } + hasTag(tag) { + return this.context.tags?.includes(tag); + } + resolveState(state) { if (this.stateful && this.hasOnset()) { console.log('stateful'); diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index b1aa852f..5d967cf1 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -2477,13 +2477,13 @@ export const hsl = register('hsl', (h, s, l, pat) => { }); /** - * Sets the id of the hap in, for filtering in the future. - * @name id + * Tags each Hap with an identifier. Good for filtering. The function populates Hap.context.tags (Array). + * @name tag * @noAutocomplete - * @param {string} id anything unique + * @param {string} tag anything unique */ -Pattern.prototype.id = function (id) { - return this.withContext((ctx) => ({ ...ctx, id })); +Pattern.prototype.tag = function (tag) { + return this.withContext((ctx) => ({ ...ctx, tags: (ctx.tags || []).concat([tag]) })); }; ////////////////////////////////////////////////////////////////////// diff --git a/packages/draw/pianoroll.mjs b/packages/draw/pianoroll.mjs index dc2faac4..53eb03f3 100644 --- a/packages/draw/pianoroll.mjs +++ b/packages/draw/pianoroll.mjs @@ -134,7 +134,7 @@ export function pianoroll({ let to = cycles * (1 - playhead); if (id) { - haps = haps.filter((hap) => hap.context.id === id); + haps = haps.filter((hap) => hap.hasTag(id)); } if (timeframeProp) { diff --git a/packages/draw/spiral.mjs b/packages/draw/spiral.mjs index e0104c29..9ea4f5a4 100644 --- a/packages/draw/spiral.mjs +++ b/packages/draw/spiral.mjs @@ -73,7 +73,7 @@ function drawSpiral(options) { } = options; if (id) { - haps = haps.filter((hap) => hap.context.id === id); + haps = haps.filter((hap) => hap.hasTag(id)); } const [w, h] = [ctx.canvas.width, ctx.canvas.height]; From 6a2486d9f2257044c6d60a16cb0305ea0e1796df Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Mar 2024 09:31:43 +0100 Subject: [PATCH 08/10] fix: id widgets by type index to prevent erasing memory in some cases --- packages/transpiler/transpiler.mjs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/transpiler/transpiler.mjs b/packages/transpiler/transpiler.mjs index d4b474d9..95156664 100644 --- a/packages/transpiler/transpiler.mjs +++ b/packages/transpiler/transpiler.mjs @@ -53,10 +53,12 @@ export function transpiler(input, options = {}) { return this.replace(sliderWithLocation(node)); } if (isWidgetMethod(node)) { + const type = node.callee.property.name; + const index = widgets.filter((w) => w.type === type).length; const widgetConfig = { to: node.end, - index: widgets.length, - type: node.callee.property.name, + index, + type, }; emitWidgets && widgets.push(widgetConfig); return this.replace(widgetWithLocation(node, widgetConfig)); From c400deac7495ca2359cfc752f1b7f62eb7783dc0 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Mar 2024 10:23:07 +0100 Subject: [PATCH 09/10] visuals now adhere to theme by default --- packages/codemirror/themes.mjs | 2 ++ packages/draw/draw.mjs | 17 ++++++++++++++++- packages/draw/pianoroll.mjs | 8 +++----- packages/draw/spiral.mjs | 11 +++++++---- packages/webaudio/scope.mjs | 7 +++++-- website/src/repl/Repl.jsx | 2 +- 6 files changed, 34 insertions(+), 13 deletions(-) diff --git a/packages/codemirror/themes.mjs b/packages/codemirror/themes.mjs index ee3e05bf..f25cf9ce 100644 --- a/packages/codemirror/themes.mjs +++ b/packages/codemirror/themes.mjs @@ -37,6 +37,7 @@ import whitescreen, { settings as whitescreenSettings } from './themes/whitescre import teletext, { settings as teletextSettings } from './themes/teletext'; import algoboy, { settings as algoboySettings } from './themes/algoboy'; import terminal, { settings as terminalSettings } from './themes/terminal'; +import { setTheme } from '@strudel/draw'; export const themes = { strudelTheme, @@ -513,6 +514,7 @@ export function activateTheme(name) { .map(([key, value]) => `--${key}: ${value} !important;`) .join('\n')} }`; + setTheme(themeSettings); // tailwind dark mode if (themeSettings.light) { document.documentElement.classList.remove('dark'); diff --git a/packages/draw/draw.mjs b/packages/draw/draw.mjs index b23b5909..cfd2ce75 100644 --- a/packages/draw/draw.mjs +++ b/packages/draw/draw.mjs @@ -68,7 +68,7 @@ Pattern.prototype.draw = function (fn, options) { fn(memory[id], _t, t, this); animationFrames[id] = requestAnimationFrame(animate); }; - requestAnimationFrame(animate); + animationFrames[id] = requestAnimationFrame(animate); return this; }; @@ -183,3 +183,18 @@ export class Drawer { } } } + +export function getComputedPropertyValue(name) { + if (typeof window === 'undefined') { + return '#fff'; + } + return getComputedStyle(document.documentElement).getPropertyValue(name); +} + +let theme = {}; +export function getTheme() { + return theme; +} +export function setTheme(_theme) { + theme = _theme; +} diff --git a/packages/draw/pianoroll.mjs b/packages/draw/pianoroll.mjs index 53eb03f3..0b3d2fd6 100644 --- a/packages/draw/pianoroll.mjs +++ b/packages/draw/pianoroll.mjs @@ -5,6 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th */ import { Pattern, noteToMidi, freqToMidi } from '@strudel/core'; +import { getTheme } from './draw.mjs'; const scale = (normalized, min, max) => normalized * (max - min) + min; const getValue = (e) => { @@ -103,11 +104,8 @@ export function pianoroll({ flipTime = 0, flipValues = 0, hideNegative = false, - // inactive = '#C9E597', - // inactive = '#FFCA28', - inactive = '#7491D2', - active = '#FFCA28', - // background = '#2A3236', + inactive = getTheme().foreground, + active = getTheme().foreground, background = 'transparent', smear = 0, playheadColor = 'white', diff --git a/packages/draw/spiral.mjs b/packages/draw/spiral.mjs index 9ea4f5a4..22f93f5d 100644 --- a/packages/draw/spiral.mjs +++ b/packages/draw/spiral.mjs @@ -1,4 +1,5 @@ import { Pattern } from '@strudel/core'; +import { getTheme } from './draw.mjs'; // polar coords -> xy function fromPolar(angle, radius, cx, cy) { @@ -19,7 +20,7 @@ function spiralSegment(options) { cy = 100, rotate = 0, thickness = margin / 2, - color = 'steelblue', + color = getTheme().foreground, cap = 'round', stretch = 1, fromOpacity = 1, @@ -61,7 +62,8 @@ function drawSpiral(options) { playheadThickness = thickness, padding = 0, steady = 1, - inactiveColor = '#ffffff50', + activeColor = getTheme().foreground, + inactiveColor = getTheme().gutterForeground, colorizeInactive = 0, fade = true, // logSpiral = true, @@ -102,7 +104,8 @@ function drawSpiral(options) { const isActive = hap.whole.begin <= time && hap.endClipped > time; const from = hap.whole.begin - time + inset; const to = hap.endClipped - time + inset - padding; - const color = hap.value?.color; + const hapColor = hap.value?.color || activeColor; + const color = colorizeInactive || isActive ? hapColor : inactiveColor; const opacity = fade ? 1 - Math.abs((hap.whole.begin - time) / min) : 1; spiralSegment({ ctx, @@ -110,7 +113,7 @@ function drawSpiral(options) { from, to, rotate, - color: colorizeInactive || isActive ? color : inactiveColor, + color, fromOpacity: opacity, toOpacity: opacity, }); diff --git a/packages/webaudio/scope.mjs b/packages/webaudio/scope.mjs index c9ee1f33..fbf4c8fc 100644 --- a/packages/webaudio/scope.mjs +++ b/packages/webaudio/scope.mjs @@ -1,5 +1,5 @@ import { Pattern, clamp } from '@strudel/core'; -import { getDrawContext } from '../draw/index.mjs'; +import { getDrawContext, getTheme } from '@strudel/draw'; import { analysers, getAnalyzerData } from 'superdough'; export function drawTimeScope( @@ -132,10 +132,13 @@ Pattern.prototype.fscope = function (config = {}) { * @example * s("sawtooth").scope() */ +let latestColor = {}; Pattern.prototype.tscope = function (config = {}) { let id = config.id ?? 1; return this.analyze(id).draw( - () => { + (haps) => { + config.color = haps[0]?.value?.color || getTheme().foreground; + latestColor[id] = config.color; clearScreen(config.smear, '0,0,0', config.ctx); drawTimeScope(analysers[id], config); }, diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index b5439846..f4c6df25 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -66,7 +66,7 @@ export function Repl({ embedded = false }) { const drawTime = [-2, 2]; const drawContext = getDrawContext(); const editor = new StrudelMirror({ - sync: true, + sync: false, defaultOutput: webaudioOutput, getTime: () => getAudioContext().currentTime, setInterval, From a8c2586da6f6a8f0abbfb1ce1f1ec364196c9dd0 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Mar 2024 10:23:46 +0100 Subject: [PATCH 10/10] fix: lint --- packages/draw/pianoroll.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/draw/pianoroll.mjs b/packages/draw/pianoroll.mjs index 0b3d2fd6..1bdf81c4 100644 --- a/packages/draw/pianoroll.mjs +++ b/packages/draw/pianoroll.mjs @@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th */ import { Pattern, noteToMidi, freqToMidi } from '@strudel/core'; -import { getTheme } from './draw.mjs'; +import { getTheme, getDrawContext } from './draw.mjs'; const scale = (normalized, min, max) => normalized * (max - min) + min; const getValue = (e) => {