1
This commit is contained in:
69
src/services/runtimeAudioFeedback.ts
Normal file
69
src/services/runtimeAudioFeedback.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
export const DEFAULT_RUNTIME_CLICK_SOUND_SRC = '/audio/ui-click-soft.wav';
|
||||
export const DEFAULT_RUNTIME_LEVEL_CLEAR_SOUND_SRC =
|
||||
'/audio/ui-level-clear.wav';
|
||||
export const DEFAULT_RUNTIME_COUNTDOWN_SOUND_SRC =
|
||||
'/audio/ui-countdown-warning.wav';
|
||||
export const DEFAULT_RUNTIME_COUNTDOWN_WARNING_THRESHOLD_MS = 5_000;
|
||||
|
||||
export const DEFAULT_RUNTIME_LEVEL_AUDIO_CONFIG = {
|
||||
clickSoundSrc: DEFAULT_RUNTIME_CLICK_SOUND_SRC,
|
||||
levelClearSoundSrc: DEFAULT_RUNTIME_LEVEL_CLEAR_SOUND_SRC,
|
||||
countdownSoundSrc: DEFAULT_RUNTIME_COUNTDOWN_SOUND_SRC,
|
||||
countdownWarningThresholdMs: DEFAULT_RUNTIME_COUNTDOWN_WARNING_THRESHOLD_MS,
|
||||
} as const;
|
||||
|
||||
const runtimeAudioCache = new Map<string, HTMLAudioElement>();
|
||||
|
||||
function clampRuntimeAudioVolume(value: number) {
|
||||
if (!Number.isFinite(value)) {
|
||||
return 0.6;
|
||||
}
|
||||
return Math.max(0, Math.min(1, value));
|
||||
}
|
||||
|
||||
export function playRuntimeClickSound(
|
||||
source = DEFAULT_RUNTIME_CLICK_SOUND_SRC,
|
||||
volume = 0.6,
|
||||
) {
|
||||
if (import.meta.env.MODE === 'test' || typeof Audio === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
const normalizedSource = source.trim();
|
||||
if (!normalizedSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
const audio =
|
||||
runtimeAudioCache.get(normalizedSource) ?? new Audio(normalizedSource);
|
||||
runtimeAudioCache.set(normalizedSource, audio);
|
||||
audio.currentTime = 0;
|
||||
audio.volume = clampRuntimeAudioVolume(volume);
|
||||
try {
|
||||
const playResult = audio.play();
|
||||
void playResult?.catch?.(() => {
|
||||
// 中文注释:浏览器可能在用户手势外拒绝播放,点击反馈不应中断主交互。
|
||||
});
|
||||
} catch {
|
||||
// 中文注释:测试环境或极端浏览器可能未实现 play,同样不能影响主交互。
|
||||
}
|
||||
}
|
||||
|
||||
export function playRuntimeLevelClearSound(volume = 0.6) {
|
||||
playRuntimeClickSound(DEFAULT_RUNTIME_LEVEL_CLEAR_SOUND_SRC, volume);
|
||||
}
|
||||
|
||||
export function playRuntimeCountdownSound(volume = 0.6) {
|
||||
playRuntimeClickSound(DEFAULT_RUNTIME_COUNTDOWN_SOUND_SRC, volume);
|
||||
}
|
||||
|
||||
export function resolveRuntimeCountdownSecondBucket(remainingMs: number) {
|
||||
if (
|
||||
!Number.isFinite(remainingMs) ||
|
||||
remainingMs <= 0 ||
|
||||
remainingMs > DEFAULT_RUNTIME_COUNTDOWN_WARNING_THRESHOLD_MS
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return Math.max(1, Math.ceil(remainingMs / 1000));
|
||||
}
|
||||
Reference in New Issue
Block a user