| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778 |
- let audioCtx: AudioContext | null = null;
- let unlocked = false;
- function getAudioContext(): AudioContext | null {
- if (typeof window === "undefined") return null;
- if (audioCtx) return audioCtx;
- const Ctx = window.AudioContext || (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;
- if (!Ctx) return null;
- audioCtx = new Ctx();
- return audioCtx;
- }
- /** 尝试解锁音频播放(需用户手势触发更稳定)。 */
- export async function unlockSound() {
- const ctx = getAudioContext();
- if (!ctx) return;
- try {
- if (ctx.state === "suspended") {
- await ctx.resume();
- }
- // 轻触发一次极低音量,避免某些浏览器仍阻止后续播放
- const osc = ctx.createOscillator();
- const gain = ctx.createGain();
- gain.gain.value = 0.00001;
- osc.type = "sine";
- osc.frequency.value = 440;
- osc.connect(gain);
- gain.connect(ctx.destination);
- osc.start();
- osc.stop(ctx.currentTime + 0.01);
- unlocked = true;
- } catch {
- // ignore
- }
- }
- function playTone(opts: { frequency: number; durationMs: number; volume: number; type?: OscillatorType; whenMs?: number }) {
- const ctx = getAudioContext();
- if (!ctx) return;
- // 未解锁时也尝试播放;如果被阻止,保持静默(不抛错)
- const startAt = ctx.currentTime + (opts.whenMs ?? 0) / 1000;
- const endAt = startAt + opts.durationMs / 1000;
- const osc = ctx.createOscillator();
- const gain = ctx.createGain();
- osc.type = opts.type ?? "sine";
- osc.frequency.setValueAtTime(opts.frequency, startAt);
- // 快速起音 + 平滑衰减(避免“哔”得刺耳)
- gain.gain.setValueAtTime(0.00001, startAt);
- gain.gain.linearRampToValueAtTime(opts.volume, startAt + 0.01);
- gain.gain.exponentialRampToValueAtTime(0.00001, endAt);
- osc.connect(gain);
- gain.connect(ctx.destination);
- try {
- osc.start(startAt);
- osc.stop(endAt);
- } catch {
- // ignore
- }
- }
- /** 新消息提示音(蜂鸣:两段短音)。 */
- export function playNotificationSound() {
- // 默认音量尽量克制;用户可通过系统音量调节
- const volume = 0.08;
- playTone({ frequency: 880, durationMs: 70, volume, type: "sine" });
- playTone({ frequency: 660, durationMs: 90, volume: volume * 0.9, type: "sine", whenMs: 90 });
- }
- /** 轻提示音(更柔和,用于非关键提示)。 */
- export function playMessageSound() {
- const volume = 0.05;
- playTone({ frequency: 520, durationMs: 80, volume, type: "triangle" });
- }
|