Files
fluxer/packages/voice_engine_v2/src/runtime/platformPort.ts
T

94 lines
2.4 KiB
TypeScript

// SPDX-License-Identifier: AGPL-3.0-or-later
export interface VoiceEngineV2ClockPort {
now(): number;
}
export interface VoiceEngineV2RandomPort {
next(): number;
}
export interface VoiceEngineV2WallClockSource {
read(): number;
}
export interface VoiceEngineV2SystemClockPort extends VoiceEngineV2ClockPort {
readonly sourceFailureCount: number;
}
const PLATFORM_CLOCK_MAX_MS = Number.MAX_SAFE_INTEGER;
interface PlatformSourceFailureLatch {
count: number;
logged: boolean;
message: string;
}
function createPlatformSourceFailureLatch(message: string): PlatformSourceFailureLatch {
return {count: 0, logged: false, message};
}
function recordPlatformSourceFailure(latch: PlatformSourceFailureLatch, error: unknown): void {
latch.count += 1;
if (latch.logged) return;
latch.logged = true;
globalThis.console.error(latch.message, error);
}
export function createVoiceEngineV2SystemClockPort(
source?: VoiceEngineV2WallClockSource,
): VoiceEngineV2SystemClockPort {
const reader = source ?? defaultSystemWallClockSource();
const failureLatch = createPlatformSourceFailureLatch(
'voice engine v2 wall clock source threw (programmer error); pinning clock readings to the last safe value',
);
let lastValue = -1;
return {
get sourceFailureCount(): number {
return failureLatch.count;
},
now(): number {
const raw = readClockSourceSafely(reader, failureLatch);
const safe = clampPlatformClockReading(raw, lastValue);
lastValue = safe;
return safe;
},
};
}
export function createVoiceEngineV2DeterministicClockPort(start = 0, stepMs = 1): VoiceEngineV2ClockPort {
let value = start;
return {
now(): number {
const current = value;
value += stepMs;
return current;
},
};
}
function defaultSystemWallClockSource(): VoiceEngineV2WallClockSource {
return {
read(): number {
return globalThis.Date.now();
},
};
}
function readClockSourceSafely(reader: VoiceEngineV2WallClockSource, latch: PlatformSourceFailureLatch): number {
try {
return reader.read();
} catch (error) {
recordPlatformSourceFailure(latch, error);
return 0;
}
}
function clampPlatformClockReading(raw: number, lastValue: number): number {
if (!Number.isFinite(raw)) return Math.max(lastValue, 0);
if (raw < 0) return Math.max(lastValue, 0);
if (raw > PLATFORM_CLOCK_MAX_MS) return PLATFORM_CLOCK_MAX_MS;
if (raw < lastValue) return lastValue;
return Math.trunc(raw);
}