audio-context-timers
Version:
A replacement for setInterval() and setTimeout() which works in unfocused windows.
84 lines (57 loc) • 2.54 kB
text/typescript
import { generateUniqueNumber } from 'fast-unique-numbers';
import { AudioBuffer, AudioBufferSourceNode, MinimalAudioContext, isSupported } from 'standardized-audio-context';
import { TFunctionMap, TTimerType } from './types';
const MINIMAL_AUDIO_CONTEXT = new MinimalAudioContext();
const AUDIO_BUFFER = new AudioBuffer({ length: 2, sampleRate: MINIMAL_AUDIO_CONTEXT.sampleRate });
const SAMPLE_DURATION = 2 / MINIMAL_AUDIO_CONTEXT.sampleRate;
const SCHEDULED_TIMEOUT_FUNCTIONS: TFunctionMap = new Map();
const SCHEDULED_INTERVAL_FUNCTIONS: TFunctionMap = new Map();
const callIntervalFunction = (id: number, type: TTimerType) => {
const functions = type === 'interval' ? SCHEDULED_INTERVAL_FUNCTIONS : SCHEDULED_TIMEOUT_FUNCTIONS;
if (functions.has(id)) {
const func = functions.get(id);
if (func !== undefined) {
func();
if (type === 'timeout') {
SCHEDULED_TIMEOUT_FUNCTIONS.delete(id);
}
}
}
};
const scheduleFunction = (id: number, delay: number, type: TTimerType) => {
const now = performance.now();
const audioBufferSourceNode = new AudioBufferSourceNode(MINIMAL_AUDIO_CONTEXT, { buffer: AUDIO_BUFFER });
audioBufferSourceNode.onended = () => {
const elapsedTime = performance.now() - now;
if (elapsedTime >= delay) {
callIntervalFunction(id, type);
} else {
scheduleFunction(id, delay - elapsedTime, type);
}
audioBufferSourceNode.disconnect(MINIMAL_AUDIO_CONTEXT.destination);
};
audioBufferSourceNode.connect(MINIMAL_AUDIO_CONTEXT.destination);
audioBufferSourceNode.start(Math.max(0, MINIMAL_AUDIO_CONTEXT.currentTime + delay / 1000 - SAMPLE_DURATION));
};
export const clearInterval = (id: number) => {
SCHEDULED_INTERVAL_FUNCTIONS.delete(id);
};
export const clearTimeout = (id: number) => {
SCHEDULED_TIMEOUT_FUNCTIONS.delete(id);
};
export { isSupported };
export const setInterval = (func: Function, delay: number) => {
const id = generateUniqueNumber(SCHEDULED_INTERVAL_FUNCTIONS);
SCHEDULED_INTERVAL_FUNCTIONS.set(id, () => {
func();
scheduleFunction(id, delay, 'interval');
});
scheduleFunction(id, delay, 'interval');
return id;
};
export const setTimeout = (func: Function, delay: number) => {
const id = generateUniqueNumber(SCHEDULED_TIMEOUT_FUNCTIONS);
SCHEDULED_TIMEOUT_FUNCTIONS.set(id, func);
scheduleFunction(id, delay, 'timeout');
return id;
};