100 lines
3 KiB
JavaScript
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);
|
|
}
|