72 lines
3.2 KiB
JavaScript
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 }); },
|
|
};
|