diff --git a/src/audio/audio.libraryItem.ts b/src/audio/audio.libraryItem.ts index f4b9366..66257fa 100644 --- a/src/audio/audio.libraryItem.ts +++ b/src/audio/audio.libraryItem.ts @@ -118,7 +118,6 @@ export class AudioLibraryItem extends LibraryItem { private async generateWaveform(): Promise { const samples = this.audioBuffer!.getChannelData(0); - // Important! Using transferable for the samples crashes Electron 8.4+ const rawWaveform = await WaveformWorker.generateWaveform(samples, AudioLibraryItem.sampleRate, 1024); const parts: IWaveformPart[] = []; diff --git a/src/globalState.ts b/src/globalState.ts index c393d96..467b90e 100644 --- a/src/globalState.ts +++ b/src/globalState.ts @@ -63,6 +63,7 @@ export const useGlobalState = defineStore("globalState", () => { snapUnits.value = defaults.snapUnits; bridgeUrl.value = ""; + graphTemplates.reset(); await library.reset(); timeline.reset(); stage.reset(); @@ -89,7 +90,9 @@ export const useGlobalState = defineStore("globalState", () => { const data: SavedState = JSON.parse(raw) as SavedState; stage.load(data.stage); graphTemplates.load(data.graphTemplates ?? []); + graphTemplates.skipSync.value = true; await library.load(data.library); + graphTemplates.skipSync.value = false; timeline.load(data.timeline); bpm.value = data.bpm ?? defaults.bpm; fps.value = data.fps ?? defaults.fps; diff --git a/src/graph/graphTemplateSync.ts b/src/graph/graphTemplateSync.ts index 4032cd7..e415fd9 100644 --- a/src/graph/graphTemplateSync.ts +++ b/src/graph/graphTemplateSync.ts @@ -1,9 +1,11 @@ +import { ref } from "vue"; import { Editor, GraphTemplate, IGraphTemplateState } from "baklavajs"; export function useGraphTemplateSync() { const token = Symbol("useGraphTemplateSync"); const targets: Editor[] = []; const templates = new Map(); + const skipSync = ref(false); let updating = false; function registerTarget(target: Editor) { @@ -12,6 +14,9 @@ export function useGraphTemplateSync() { updateTemplate(template.save()); }); target.events.removeGraphTemplate.subscribe(token, (template) => { + if (target.loading) { + return; + } removeTemplate(template.id); }); target.graphTemplateEvents.updated.subscribe(token, (_, template) => { @@ -34,9 +39,9 @@ export function useGraphTemplateSync() { function unregisterTarget(target: Editor) { const index = targets.indexOf(target); if (index !== -1) { + const target = targets[index]; targets.splice(index, 1); - const target = targets[index]; target.events.addGraphTemplate.unsubscribe(token); target.events.removeGraphTemplate.unsubscribe(token); target.graphTemplateEvents.updated.unsubscribe(token); @@ -54,8 +59,17 @@ export function useGraphTemplateSync() { } } + function reset() { + templates.clear(); + while (targets.length > 0) { + unregisterTarget(targets[0]); + } + updating = false; + skipSync.value = false; + } + function updateTemplate(template: IGraphTemplateState) { - if (updating) { + if (updating || skipSync.value) { return; } @@ -76,7 +90,7 @@ export function useGraphTemplateSync() { } function removeTemplate(id: string) { - if (updating) { + if (updating || skipSync.value) { return; } @@ -91,5 +105,5 @@ export function useGraphTemplateSync() { updating = false; } - return { registerTarget, unregisterTarget, save, load }; + return { skipSync, registerTarget, unregisterTarget, save, load, reset }; } diff --git a/src/graph/nodes/input/TrackItemProgressNode.ts b/src/graph/nodes/input/TrackItemProgressNode.ts new file mode 100644 index 0000000..3da0bfc --- /dev/null +++ b/src/graph/nodes/input/TrackItemProgressNode.ts @@ -0,0 +1,19 @@ +import { defineNode } from "baklavajs"; +import { NumberInterface } from "../../interfaces"; +import { LmsCalculationContext } from "../../types"; + +export const TrackItemProgressNode = defineNode({ + type: "Track Item Progress", + inputs: { + min: () => new NumberInterface("Min", 0), + max: () => new NumberInterface("Max", 1), + }, + outputs: { + value: () => new NumberInterface("Value", 0).setComponent(undefined), + }, + calculate(inputs, context: LmsCalculationContext) { + const { min, max } = inputs; + const value = min + (max - min) * context.globalValues.relativeTrackItemProgress; + return { value }; + }, +}); diff --git a/src/graph/nodes/input/index.ts b/src/graph/nodes/input/index.ts index 80b79b4..d4bba19 100644 --- a/src/graph/nodes/input/index.ts +++ b/src/graph/nodes/input/index.ts @@ -3,3 +3,4 @@ export * from "./LfoNode"; export * from "./PatternNode"; export * from "./PeakNode"; export * from "./SpectrumNode"; +export * from "./TrackItemProgressNode"; diff --git a/src/graph/types.ts b/src/graph/types.ts index 9dc440c..2a74f0b 100644 --- a/src/graph/types.ts +++ b/src/graph/types.ts @@ -9,6 +9,7 @@ export interface ICalculationData { timeDomainData: Float32Array; frequencyData: Float32Array; trackValues: Map; + relativeTrackItemProgress: number; } export type LmsCalculationContext = CalculationContext; diff --git a/src/timeline/timelineProcessor.ts b/src/timeline/timelineProcessor.ts index 2902d8c..60a00a1 100644 --- a/src/timeline/timelineProcessor.ts +++ b/src/timeline/timelineProcessor.ts @@ -93,7 +93,7 @@ export class TimelineProcessor { const audioData = this.audioProcessor!.getAudioData(); const uncontrolledFixtures = new Set(this.stage.fixtures.values()) as Set; - const calculationData: ICalculationData = { + const calculationData: Omit = { resolution: this.globalState.resolution, fps: this.globalState.fps, position: unit, @@ -105,7 +105,8 @@ export class TimelineProcessor { const graphs = currentActiveItems.filter((i) => this.isType(i, LibraryItemType.GRAPH)); for (const g of graphs) { try { - const results = await this.processGraph(g, unit, calculationData); + const relativeTrackItemProgress = (unit - g.start) / (g.end - g.start); + const results = await this.processGraph(g, unit, { ...calculationData, relativeTrackItemProgress }); if (g.libraryItem.error) { g.libraryItem.error = ""; } diff --git a/src/utils/comlinkVueTransferHandler.ts b/src/utils/comlinkVueTransferHandler.ts index 4d34c0c..1a84d3b 100644 --- a/src/utils/comlinkVueTransferHandler.ts +++ b/src/utils/comlinkVueTransferHandler.ts @@ -3,7 +3,9 @@ import * as Comlink from "comlink"; const vueReactiveTransferHandler: Comlink.TransferHandler = { canHandle: ((x: any) => { - return isReactive(x) || (x && typeof x === "object" && Object.values(x).some((v) => isReactive(v))); + return ( + !(x instanceof Float32Array) && (isReactive(x) || (x && typeof x === "object" && Object.values(x).some((v) => isReactive(v)))) + ); }) as (x: any) => x is any, serialize: (x) => [JSON.parse(JSON.stringify(x)), []], deserialize: (x) => x,