donau2space-dev/js/audio.js

100 lines
3 KiB
JavaScript

// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// DONAU2SPACE // DEV ENTITY — Audio Synth
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
let ctx = null;
function getCtx() {
if (!ctx) {
try { ctx = new (window.AudioContext || window.webkitAudioContext)(); }
catch { return null; }
}
if (ctx.state === 'suspended') ctx.resume().catch(() => {});
return ctx;
}
export function beep(freq = 880, ms = 70, vol = 0.04, type = 'square') {
const ac = getCtx();
if (!ac) return;
const osc = ac.createOscillator();
const gain = ac.createGain();
osc.type = type;
osc.frequency.value = freq;
gain.gain.setValueAtTime(vol, ac.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, ac.currentTime + ms / 1000);
osc.connect(gain);
gain.connect(ac.destination);
osc.start();
osc.stop(ac.currentTime + ms / 1000 + 0.05);
}
export function keyClick() {
beep(800 + Math.random() * 400, 15, 0.008, 'sine');
}
export function errorBeep() {
beep(220, 90, 0.05, 'square');
setTimeout(() => beep(180, 120, 0.04, 'square'), 100);
}
export function successChime() {
beep(523, 60, 0.03, 'sine');
setTimeout(() => beep(659, 60, 0.03, 'sine'), 70);
setTimeout(() => beep(784, 80, 0.03, 'sine'), 140);
}
export function warningTone() {
beep(440, 150, 0.04, 'sawtooth');
setTimeout(() => beep(440, 150, 0.04, 'sawtooth'), 200);
}
export function sirenBurst(duration = 800) {
const ac = getCtx();
if (!ac) return;
const osc = ac.createOscillator();
const gain = ac.createGain();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(200, ac.currentTime);
osc.frequency.linearRampToValueAtTime(600, ac.currentTime + duration / 2000);
osc.frequency.linearRampToValueAtTime(200, ac.currentTime + duration / 1000);
gain.gain.setValueAtTime(0.03, ac.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, ac.currentTime + duration / 1000);
osc.connect(gain);
gain.connect(ac.destination);
osc.start();
osc.stop(ac.currentTime + duration / 1000 + 0.05);
}
export function transmitSound() {
const ac = getCtx();
if (!ac) return;
for (let i = 0; i < 8; i++) {
setTimeout(() => {
beep(1200 + Math.random() * 800, 30, 0.015, 'sine');
}, i * 40);
}
}
export function droneStart() {
const ac = getCtx();
if (!ac) return null;
const osc = ac.createOscillator();
const gain = ac.createGain();
osc.type = 'sine';
osc.frequency.value = 55;
gain.gain.value = 0;
gain.gain.linearRampToValueAtTime(0.015, ac.currentTime + 2);
osc.connect(gain);
gain.connect(ac.destination);
osc.start();
return { osc, gain, stop: () => {
gain.gain.linearRampToValueAtTime(0.001, ac.currentTime + 1);
setTimeout(() => { try { osc.stop(); } catch {} }, 1200);
}};
}
export function bootSound() {
beep(130, 200, 0.02, 'sine');
setTimeout(() => beep(165, 200, 0.02, 'sine'), 200);
setTimeout(() => beep(196, 300, 0.025, 'sine'), 400);
}