fixing timing issues

This commit is contained in:
André Michelle
2026-03-20 13:37:39 +01:00
parent c3029d2c9d
commit 272571f3bc
5 changed files with 50 additions and 19 deletions
@@ -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<void>
}
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)
@@ -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<void>
}
export const subscribeScriptParams = (parametric: ParameterAdapterSet,
codeField: StringField,
parametersField: Field<Pointers.Parameter>): Terminable => {
parametersField: Field<Pointers.Parameter>): ScriptParamsBinding => {
const cachedDeclarations = new Map<string, ParamDeclaration>()
return Terminable.many(
const codeChangedNotifier = new Notifier<void>()
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}
}
}
@@ -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<void>
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<Pointers.AudioEffectHost> {return this.#box.host}
get parameters(): ParameterAdapterSet {return this.#parametric}
get codeChanged(): Observable<void> {return this.#codeChanged}
deviceHost(): DeviceHost {
return this.#context.boxAdapters
@@ -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<void>
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<void> {return this.#codeChanged}
deviceHost(): DeviceHost {
return this.#context.boxAdapters
@@ -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<void>
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<Pointers.MIDIEffectHost> {return this.#box.host}
get parameters(): ParameterAdapterSet {return this.#parametric}
get codeChanged(): Observable<void> {return this.#codeChanged}
deviceHost(): DeviceHost {
return this.#context.boxAdapters