102 lines
2.5 KiB
TypeScript
102 lines
2.5 KiB
TypeScript
/** Infrastructure adapter: Web Audio API sound effects. */
|
|
|
|
let audioContext: AudioContext | null = null;
|
|
|
|
function getAudioContext(): AudioContext | null {
|
|
if (typeof window === 'undefined') return null;
|
|
if (!audioContext) {
|
|
try {
|
|
audioContext = new (window.AudioContext || (window as Window & { webkitAudioContext?: typeof AudioContext }).webkitAudioContext!)();
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
return audioContext;
|
|
}
|
|
|
|
export function playHoverSound() {
|
|
const ctx = getAudioContext();
|
|
if (!ctx) return;
|
|
|
|
try {
|
|
const osc = ctx.createOscillator();
|
|
const gain = ctx.createGain();
|
|
const filter = ctx.createBiquadFilter();
|
|
|
|
osc.type = 'sine';
|
|
osc.frequency.value = 1100;
|
|
filter.type = 'lowpass';
|
|
filter.frequency.value = 1400;
|
|
gain.gain.value = 0.04;
|
|
|
|
osc.connect(filter);
|
|
filter.connect(gain);
|
|
gain.connect(ctx.destination);
|
|
osc.start();
|
|
gain.gain.linearRampToValueAtTime(0.001, ctx.currentTime + 0.12);
|
|
setTimeout(() => osc.stop(), 150);
|
|
} catch {
|
|
// Silent fail
|
|
}
|
|
}
|
|
|
|
export function playAddSound() {
|
|
const ctx = getAudioContext();
|
|
if (!ctx) return;
|
|
|
|
try {
|
|
const osc = ctx.createOscillator();
|
|
const gain = ctx.createGain();
|
|
const filter = ctx.createBiquadFilter();
|
|
|
|
osc.type = 'sine';
|
|
osc.frequency.value = 1080;
|
|
filter.type = 'lowpass';
|
|
filter.frequency.value = 1550;
|
|
gain.gain.value = 0.022;
|
|
|
|
osc.connect(filter);
|
|
filter.connect(gain);
|
|
gain.connect(ctx.destination);
|
|
osc.start();
|
|
gain.gain.linearRampToValueAtTime(0.001, ctx.currentTime + 0.08);
|
|
setTimeout(() => osc.stop(), 120);
|
|
} catch {
|
|
// Silent fail
|
|
}
|
|
}
|
|
|
|
export function playSuccessSound() {
|
|
const ctx = getAudioContext();
|
|
if (!ctx) return;
|
|
|
|
try {
|
|
const notes = [523, 659, 784, 1046];
|
|
notes.forEach((freq, i) => {
|
|
setTimeout(() => {
|
|
try {
|
|
const osc = ctx.createOscillator();
|
|
const gain = ctx.createGain();
|
|
const filter = ctx.createBiquadFilter();
|
|
|
|
osc.type = 'sine';
|
|
osc.frequency.value = freq;
|
|
filter.type = 'lowpass';
|
|
filter.frequency.value = 2000;
|
|
gain.gain.value = 0.07;
|
|
|
|
osc.connect(filter);
|
|
filter.connect(gain);
|
|
gain.connect(ctx.destination);
|
|
osc.start();
|
|
gain.gain.linearRampToValueAtTime(0.001, ctx.currentTime + 0.5);
|
|
setTimeout(() => osc.stop(), 600);
|
|
} catch {
|
|
// Silent fail
|
|
}
|
|
}, i * 120);
|
|
});
|
|
} catch {
|
|
// Silent fail
|
|
}
|
|
} |