this repo has no description
1let audioCtx: AudioContext | null = null;
2
3function getAudioContext(): AudioContext {
4 if (!audioCtx) {
5 audioCtx = new AudioContext();
6 }
7 if (audioCtx.state === 'suspended') {
8 audioCtx.resume();
9 }
10 return audioCtx;
11}
12
13function playTone(
14 ctx: AudioContext,
15 frequency: number,
16 startTime: number,
17 duration: number,
18 type: OscillatorType = 'sine',
19 volume = 0.3,
20) {
21 const osc = ctx.createOscillator();
22 const gain = ctx.createGain();
23
24 osc.type = type;
25 osc.frequency.value = frequency;
26 gain.gain.setValueAtTime(volume, startTime);
27 gain.gain.exponentialRampToValueAtTime(0.001, startTime + duration);
28
29 osc.connect(gain);
30 gain.connect(ctx.destination);
31 osc.start(startTime);
32 osc.stop(startTime + duration);
33}
34
35export function playCorrectSound() {
36 try {
37 const ctx = getAudioContext();
38 const now = ctx.currentTime;
39 // Cheerful ascending two-note ding
40 playTone(ctx, 523.25, now, 0.15, 'sine', 0.25); // C5
41 playTone(ctx, 659.25, now + 0.12, 0.2, 'sine', 0.25); // E5
42 } catch { /* audio unavailable */ }
43}
44
45export function playIncorrectSound() {
46 try {
47 const ctx = getAudioContext();
48 const now = ctx.currentTime;
49 // Gentle descending two-note
50 playTone(ctx, 330, now, 0.15, 'triangle', 0.2); // E4
51 playTone(ctx, 262, now + 0.12, 0.25, 'triangle', 0.2); // C4
52 } catch { /* audio unavailable */ }
53}
54
55export function playCompletionSound() {
56 try {
57 const ctx = getAudioContext();
58 const now = ctx.currentTime;
59 // Ascending fanfare
60 playTone(ctx, 523.25, now, 0.15, 'sine', 0.2); // C5
61 playTone(ctx, 659.25, now + 0.1, 0.15, 'sine', 0.2); // E5
62 playTone(ctx, 783.99, now + 0.2, 0.15, 'sine', 0.2); // G5
63 playTone(ctx, 1046.5, now + 0.3, 0.35, 'sine', 0.25); // C6
64 } catch { /* audio unavailable */ }
65}