From 5be8638e22fcb6dd2c33992377e06c020d0960cc Mon Sep 17 00:00:00 2001 From: danielku15 Date: Wed, 7 Oct 2026 12:49:48 +0200 Subject: [PATCH 1/2] test(player): cover late midiLoaded registration with the worker player --- .../test/audio/AlphaSynthWebWorkerApi.test.ts | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 packages/alphatab/test/audio/AlphaSynthWebWorkerApi.test.ts diff --git a/packages/alphatab/test/audio/AlphaSynthWebWorkerApi.test.ts b/packages/alphatab/test/audio/AlphaSynthWebWorkerApi.test.ts new file mode 100644 index 000000000..2aebe3b29 --- /dev/null +++ b/packages/alphatab/test/audio/AlphaSynthWebWorkerApi.test.ts @@ -0,0 +1,61 @@ +/** + * Tests for the main-thread side of the worker based player. Feeds worker messages + * directly as plain objects, so web-only. + * + * @target web + */ + +import { AlphaSynthWebWorkerApi } from '@coderline/alphatab/platform/worker/AlphaSynthWebWorkerApi'; +import type { + IAlphaSynthWorker, + IAlphaSynthWorkerMessage +} from '@coderline/alphatab/platform/worker/AlphaTabWorkerProtocol'; +import { Settings } from '@coderline/alphatab/Settings'; +import { AlphaSynthWrapper } from '@coderline/alphatab/synth/AlphaSynthWrapper'; +import { PositionChangedEventArgs } from '@coderline/alphatab/synth/PositionChangedEventArgs'; +import { TestOutput } from 'test/audio/TestOutput'; +import { describe, expect, it } from 'vitest'; + +class TestSynthWorker implements IAlphaSynthWorker { + public postMessage(_message: IAlphaSynthWorkerMessage): void { + // nothing to do + } + + public addEventListener(_event: 'message', _handler: (ev: MessageEvent) => void): void { + // nothing to do + } + + public removeEventListener( + _event: 'message', + _handler: (ev: MessageEvent) => void + ): void { + // nothing to do + } + + public terminate(): void { + // nothing to do + } +} + +describe('AlphaSynthWebWorkerApiTests', () => { + it('midi-loaded-late-registration', () => { + const workerApi = new AlphaSynthWebWorkerApi(new TestOutput(), new Settings(), new TestSynthWorker()); + const wrapper = new AlphaSynthWrapper(); + wrapper.instance = workerApi; + + workerApi.handleWorkerMessage({ + data: { + cmd: 'alphaSynth.midiLoaded', + args: new PositionChangedEventArgs(0, 2000, 0, 3840, false, 120, 120) + } + } as MessageEvent); + + expect(wrapper.loadedMidiInfo?.endTick).toBe(3840); + + const received: PositionChangedEventArgs[] = []; + wrapper.midiLoaded.on(e => received.push(e)); + expect(received.length).toBe(1); + expect(received[0].endTick).toBe(3840); + expect(received[0].endTime).toBe(2000); + }); +}); From 3c16949be9f4df44b6d2b2ef7ea7e22892cde783 Mon Sep 17 00:00:00 2001 From: danielku15 Date: Wed, 7 Oct 2026 12:51:34 +0200 Subject: [PATCH 2/2] fix(player): return stored loaded MIDI info from worker player API The loadedMidiInfo getter returned itself instead of the backing field, so any late midiLoaded registration recursed until the stack overflowed. Fixes #2862 --- packages/alphatab/src/platform/worker/AlphaSynthWebWorkerApi.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/alphatab/src/platform/worker/AlphaSynthWebWorkerApi.ts b/packages/alphatab/src/platform/worker/AlphaSynthWebWorkerApi.ts index 2c757fd2c..2905ba46a 100644 --- a/packages/alphatab/src/platform/worker/AlphaSynthWebWorkerApi.ts +++ b/packages/alphatab/src/platform/worker/AlphaSynthWebWorkerApi.ts @@ -144,7 +144,7 @@ export class AlphaSynthWebWorkerApi implements IAlphaSynth { } public get loadedMidiInfo(): PositionChangedEventArgs | undefined { - return this.loadedMidiInfo; + return this._loadedMidiInfo; } public get currentPosition(): PositionChangedEventArgs {