From 272571f3bc33ac37760efd301a623f9500d41b86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Michelle?= Date: Fri, 20 Mar 2026 13:37:39 +0100 Subject: [PATCH] fixing timing issues --- .../src/ui/devices/ScriptDeviceEditor.tsx | 28 +++++++++++++------ .../adapters/src/ScriptParamDeclaration.ts | 17 ++++++++--- .../WerkstattDeviceBoxAdapter.ts | 8 ++++-- .../instruments/ApparatDeviceBoxAdapter.ts | 8 ++++-- .../midi-effects/SpielwerkDeviceBoxAdapter.ts | 8 ++++-- 5 files changed, 50 insertions(+), 19 deletions(-) diff --git a/packages/app/studio/src/ui/devices/ScriptDeviceEditor.tsx b/packages/app/studio/src/ui/devices/ScriptDeviceEditor.tsx index 3fccad23..07ea74a4 100644 --- a/packages/app/studio/src/ui/devices/ScriptDeviceEditor.tsx +++ b/packages/app/studio/src/ui/devices/ScriptDeviceEditor.tsx @@ -1,6 +1,6 @@ import css from "./ScriptDeviceEditor.sass?inline" import {DeviceBoxAdapter, DeviceHost, ParameterAdapterSet, ScriptCompiler, ScriptParamDeclaration} from "@opendaw/studio-adapters" -import {asInstanceOf, Editing, EmptyExec, isDefined, Lifecycle, MutableObservableValue, Nullable, ObservableValue, Observer, Subscription, Terminable, Terminator, UUID} from "@opendaw/lib-std" +import {asInstanceOf, Editing, EmptyExec, isDefined, Lifecycle, MutableObservableValue, Nullable, Observable, ObservableValue, Observer, Subscription, Terminable, Terminator, UUID} from "@opendaw/lib-std" import {AutomatableParameterFieldAdapter} from "@opendaw/studio-adapters" import {Promises} from "@opendaw/lib-runtime" import {createElement} from "@opendaw/lib-jsx" @@ -37,6 +37,7 @@ const boolModel = (editing: Editing, parameter: AutomatableParameterFieldAdapter type ScriptAdapter = DeviceBoxAdapter & { readonly box: ScriptCompiler.DeviceBox readonly parameters: ParameterAdapterSet + readonly codeChanged: Observable } export type ScriptDeviceEditorConfig = { @@ -67,15 +68,21 @@ export const ScriptDeviceEditor = ({lifecycle, service, adapter, deviceHost, con const box = adapter.box const storedCode = box.code.getValue() const userCode = storedCode.length > 0 ? compiler.stripHeader(storedCode) : config.defaultCode + let compiling = false const compile = async (code: string) => { - const result = await Promises.tryCatch(compiler.compile(service.audioContext, editing, box, code)) - if (result.status === "resolved") { - errorIcon.classList.add("hidden") - errorIcon.title = "" - } else { - errorIcon.classList.remove("hidden") - errorIcon.title = String(result.error) - throw result.error + compiling = true + try { + const result = await Promises.tryCatch(compiler.compile(service.audioContext, editing, box, code)) + if (result.status === "resolved") { + errorIcon.classList.add("hidden") + errorIcon.title = "" + } else { + errorIcon.classList.remove("hidden") + errorIcon.title = String(result.error) + throw result.error + } + } finally { + compiling = false } } if (storedCode.length > 0) { @@ -132,6 +139,9 @@ export const ScriptDeviceEditor = ({lifecycle, service, adapter, deviceHost, con if (isActive) {service.closeCodeEditor()} } }, + adapter.codeChanged.subscribe(() => { + if (!compiling) {compiler.load(service.audioContext, box).finally(EmptyExec)} + }), service.activeCodeEditor.catchupAndSubscribe(option => { const isActive = option.map(state => UUID.equals(state.handler.uuid, adapter.uuid)).unwrapOrElse(false) toggleEditorButton.classList.toggle("active", isActive) diff --git a/packages/studio/adapters/src/ScriptParamDeclaration.ts b/packages/studio/adapters/src/ScriptParamDeclaration.ts index 808f969f..dbda40e1 100644 --- a/packages/studio/adapters/src/ScriptParamDeclaration.ts +++ b/packages/studio/adapters/src/ScriptParamDeclaration.ts @@ -1,4 +1,4 @@ -import {asInstanceOf, isDefined, Nullable, StringMapping, Terminable, ValueMapping} from "@opendaw/lib-std" +import {asInstanceOf, isDefined, Notifier, Nullable, Observable, StringMapping, Terminable, ValueMapping} from "@opendaw/lib-std" import {Field, StringField} from "@opendaw/lib-box" import {Pointers} from "@opendaw/studio-enums" import {WerkstattParameterBox} from "@opendaw/studio-boxes" @@ -182,11 +182,17 @@ export namespace ScriptParamDeclaration { stringMapping: resolveStringMapping(declaration) }) + export type ScriptParamsBinding = { + readonly terminable: Terminable + readonly codeChanged: Observable + } + export const subscribeScriptParams = (parametric: ParameterAdapterSet, codeField: StringField, - parametersField: Field): Terminable => { + parametersField: Field): ScriptParamsBinding => { const cachedDeclarations = new Map() - return Terminable.many( + const codeChangedNotifier = new Notifier() + const terminable = Terminable.many( parametersField.pointerHub.catchupAndSubscribe({ onAdded: (({box: parameterBox}) => { const paramBox = asInstanceOf(parameterBox, WerkstattParameterBox) @@ -219,7 +225,10 @@ export namespace ScriptParamDeclaration { adapter.updateMappings(valueMapping, stringMapping) cachedDeclarations.set(adapter.name, newDeclaration) } - }) + codeChangedNotifier.notify() + }), + codeChangedNotifier ) + return {terminable, codeChanged: codeChangedNotifier} } } diff --git a/packages/studio/adapters/src/devices/audio-effects/WerkstattDeviceBoxAdapter.ts b/packages/studio/adapters/src/devices/audio-effects/WerkstattDeviceBoxAdapter.ts index c80a498b..be49d29f 100644 --- a/packages/studio/adapters/src/devices/audio-effects/WerkstattDeviceBoxAdapter.ts +++ b/packages/studio/adapters/src/devices/audio-effects/WerkstattDeviceBoxAdapter.ts @@ -1,4 +1,4 @@ -import {Option, Terminator, UUID} from "@opendaw/lib-std" +import {Observable, Option, Terminator, UUID} from "@opendaw/lib-std" import {Address, BooleanField, Int32Field, PointerField, StringField} from "@opendaw/lib-box" import {WerkstattDeviceBox} from "@opendaw/studio-boxes" import {Pointers} from "@opendaw/studio-enums" @@ -20,12 +20,15 @@ export class WerkstattDeviceBoxAdapter implements AudioEffectDeviceAdapter { readonly #context: BoxAdaptersContext readonly #box: WerkstattDeviceBox readonly #parametric: ParameterAdapterSet + readonly #codeChanged: Observable constructor(context: BoxAdaptersContext, box: WerkstattDeviceBox) { this.#context = context this.#box = box this.#parametric = this.#terminator.own(new ParameterAdapterSet(this.#context)) - this.#terminator.own(ScriptParamDeclaration.subscribeScriptParams(this.#parametric, box.code, box.parameters)) + const {terminable, codeChanged} = ScriptParamDeclaration.subscribeScriptParams(this.#parametric, box.code, box.parameters) + this.#terminator.own(terminable) + this.#codeChanged = codeChanged } get box(): WerkstattDeviceBox {return this.#box} @@ -37,6 +40,7 @@ export class WerkstattDeviceBoxAdapter implements AudioEffectDeviceAdapter { get minimizedField(): BooleanField {return this.#box.minimized} get host(): PointerField {return this.#box.host} get parameters(): ParameterAdapterSet {return this.#parametric} + get codeChanged(): Observable {return this.#codeChanged} deviceHost(): DeviceHost { return this.#context.boxAdapters diff --git a/packages/studio/adapters/src/devices/instruments/ApparatDeviceBoxAdapter.ts b/packages/studio/adapters/src/devices/instruments/ApparatDeviceBoxAdapter.ts index 3b6bb6ea..0ba7837f 100644 --- a/packages/studio/adapters/src/devices/instruments/ApparatDeviceBoxAdapter.ts +++ b/packages/studio/adapters/src/devices/instruments/ApparatDeviceBoxAdapter.ts @@ -1,4 +1,4 @@ -import {Option, Terminator, UUID} from "@opendaw/lib-std" +import {Observable, Option, Terminator, UUID} from "@opendaw/lib-std" import {Address, BooleanField, StringField} from "@opendaw/lib-box" import {ApparatDeviceBox} from "@opendaw/studio-boxes" import {DeviceHost, Devices, InstrumentDeviceBoxAdapter} from "../../DeviceAdapter" @@ -20,12 +20,15 @@ export class ApparatDeviceBoxAdapter implements InstrumentDeviceBoxAdapter { readonly #context: BoxAdaptersContext readonly #box: ApparatDeviceBox readonly #parametric: ParameterAdapterSet + readonly #codeChanged: Observable constructor(context: BoxAdaptersContext, box: ApparatDeviceBox) { this.#context = context this.#box = box this.#parametric = this.#terminator.own(new ParameterAdapterSet(this.#context)) - this.#terminator.own(ScriptParamDeclaration.subscribeScriptParams(this.#parametric, box.code, box.parameters)) + const {terminable, codeChanged} = ScriptParamDeclaration.subscribeScriptParams(this.#parametric, box.code, box.parameters) + this.#terminator.own(terminable) + this.#codeChanged = codeChanged } get box(): ApparatDeviceBox {return this.#box} @@ -38,6 +41,7 @@ export class ApparatDeviceBoxAdapter implements InstrumentDeviceBoxAdapter { get minimizedField(): BooleanField {return this.#box.minimized} get acceptsMidiEvents(): boolean {return true} get parameters(): ParameterAdapterSet {return this.#parametric} + get codeChanged(): Observable {return this.#codeChanged} deviceHost(): DeviceHost { return this.#context.boxAdapters diff --git a/packages/studio/adapters/src/devices/midi-effects/SpielwerkDeviceBoxAdapter.ts b/packages/studio/adapters/src/devices/midi-effects/SpielwerkDeviceBoxAdapter.ts index 5558bf90..5703a108 100644 --- a/packages/studio/adapters/src/devices/midi-effects/SpielwerkDeviceBoxAdapter.ts +++ b/packages/studio/adapters/src/devices/midi-effects/SpielwerkDeviceBoxAdapter.ts @@ -1,4 +1,4 @@ -import {Option, Terminator, UUID} from "@opendaw/lib-std" +import {Observable, Option, Terminator, UUID} from "@opendaw/lib-std" import {Address, BooleanField, Int32Field, PointerField, StringField} from "@opendaw/lib-box" import {SpielwerkDeviceBox} from "@opendaw/studio-boxes" import {Pointers} from "@opendaw/studio-enums" @@ -19,12 +19,15 @@ export class SpielwerkDeviceBoxAdapter implements MidiEffectDeviceAdapter { readonly #context: BoxAdaptersContext readonly #box: SpielwerkDeviceBox readonly #parametric: ParameterAdapterSet + readonly #codeChanged: Observable constructor(context: BoxAdaptersContext, box: SpielwerkDeviceBox) { this.#context = context this.#box = box this.#parametric = this.#terminator.own(new ParameterAdapterSet(this.#context)) - this.#terminator.own(ScriptParamDeclaration.subscribeScriptParams(this.#parametric, box.code, box.parameters)) + const {terminable, codeChanged} = ScriptParamDeclaration.subscribeScriptParams(this.#parametric, box.code, box.parameters) + this.#terminator.own(terminable) + this.#codeChanged = codeChanged } get box(): SpielwerkDeviceBox {return this.#box} @@ -36,6 +39,7 @@ export class SpielwerkDeviceBoxAdapter implements MidiEffectDeviceAdapter { get minimizedField(): BooleanField {return this.#box.minimized} get host(): PointerField {return this.#box.host} get parameters(): ParameterAdapterSet {return this.#parametric} + get codeChanged(): Observable {return this.#codeChanged} deviceHost(): DeviceHost { return this.#context.boxAdapters