Files
kidgo/js/audio.js
T
2026-10-20 22:21:26 +08:00

72 lines
3.2 KiB
JavaScript

// 纯 WebAudio 合成音效,无外部资源
let ctx = null, master = null;
export const Audio2 = { enabled: true, volume: 0.5 };
function ac() {
if (!ctx) {
const AC = window.AudioContext || window.webkitAudioContext;
if (!AC) return null;
ctx = new AC();
master = ctx.createGain();
master.gain.value = Audio2.volume;
master.connect(ctx.destination);
}
if (ctx.state === 'suspended') ctx.resume();
return ctx;
}
export function unlockAudio() { ac(); }
function tone({ f = 440, f2 = null, d = 0.1, type = 'square', vol = 0.25, delay = 0, decay = 0 }) {
if (!Audio2.enabled) return;
const c = ac(); if (!c) return;
const t0 = c.currentTime + delay;
const o = c.createOscillator();
const g = c.createGain();
o.type = type;
o.frequency.setValueAtTime(f, t0);
if (f2 !== null) o.frequency.exponentialRampToValueAtTime(Math.max(20, f2), t0 + d);
g.gain.setValueAtTime(0.0001, t0);
g.gain.linearRampToValueAtTime(vol, t0 + 0.008);
const end = t0 + d + decay;
g.gain.exponentialRampToValueAtTime(0.0001, end);
o.connect(g); g.connect(master);
o.start(t0); o.stop(end + 0.02);
}
function noise({ d = 0.2, vol = 0.25, delay = 0, hp = 400 }) {
if (!Audio2.enabled) return;
const c = ac(); if (!c) return;
const t0 = c.currentTime + delay;
const len = Math.floor(c.sampleRate * d);
const buf = c.createBuffer(1, len, c.sampleRate);
const data = buf.getChannelData(0);
for (let i = 0; i < len; i++) data[i] = (Math.random() * 2 - 1) * (1 - i / len);
const src = c.createBufferSource(); src.buffer = buf;
const f = c.createBiquadFilter(); f.type = 'highpass'; f.frequency.value = hp;
const g = c.createGain(); g.gain.value = vol;
src.connect(f); f.connect(g); g.connect(master);
src.start(t0);
}
export const sfx = {
jump() { tone({ f: 380, f2: 700, d: 0.09, type: 'square', vol: 0.16 }); },
djump() { tone({ f: 520, f2: 900, d: 0.09, type: 'square', vol: 0.14 }); },
shoot() { tone({ f: 900, f2: 260, d: 0.07, type: 'square', vol: 0.14 }); noise({ d: 0.05, vol: 0.06, hp: 2000 }); },
death() { tone({ f: 620, f2: 60, d: 0.55, type: 'sawtooth', vol: 0.26 });
noise({ d: 0.35, vol: 0.16, hp: 300, delay: 0.02 }); },
save() { tone({ f: 700, d: 0.08, type: 'triangle', vol: 0.2 });
tone({ f: 1050, d: 0.13, type: 'triangle', vol: 0.18, delay: 0.07 }); },
goal() { [523, 659, 784, 1047].forEach((f, i) =>
tone({ f, d: 0.16, type: 'triangle', vol: 0.2, delay: i * 0.1 })); },
warp() { tone({ f: 300, f2: 1400, d: 0.2, type: 'sine', vol: 0.18 }); },
hit() { tone({ f: 200, f2: 90, d: 0.08, type: 'square', vol: 0.16 }); },
bossHit() { tone({ f: 260, f2: 140, d: 0.1, type: 'sawtooth', vol: 0.14 }); },
bossDie() { [400, 330, 260, 180, 110].forEach((f, i) =>
tone({ f, d: 0.22, type: 'sawtooth', vol: 0.22, delay: i * 0.11 }));
noise({ d: 0.8, vol: 0.2, hp: 150, delay: 0.1 }); },
menu() { tone({ f: 620, d: 0.05, type: 'square', vol: 0.12 }); },
vanish() { tone({ f: 700, f2: 300, d: 0.12, type: 'sine', vol: 0.1 }); },
troll() { tone({ f: 180, f2: 900, d: 0.3, type: 'sine', vol: 0.18 }); },
splash() { noise({ d: 0.18, vol: 0.12, hp: 800 }); },
};