mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
94 lines
2.4 KiB
TypeScript
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);
|
|
}
|