tarinai/js/audio.js

43 lines
1.6 KiB
JavaScript
Raw Normal View History

2026-06-18 18:59:56 +09:00
"use strict";
const audio = {
enabled: false,
ctx: null,
ensure() {
const AudioClass = window.AudioContext || window.webkitAudioContext;
if (!AudioClass) return null;
if (!this.ctx) this.ctx = new AudioClass();
if (this.ctx.state === "suspended") this.ctx.resume();
return this.ctx;
},
toggle() {
this.enabled = !this.enabled;
if (this.enabled) this.ensure();
return this.enabled;
},
tone(freq, dur = 0.08, type = "sine", gain = 0.035, slide = 0) {
if (!this.enabled) return;
const ctx = this.ensure();
if (!ctx) return;
const t0 = ctx.currentTime;
const osc = ctx.createOscillator();
const g = ctx.createGain();
osc.type = type;
osc.frequency.setValueAtTime(freq, t0);
if (slide) osc.frequency.linearRampToValueAtTime(Math.max(40, freq + slide), t0 + dur);
g.gain.setValueAtTime(0.0001, t0);
g.gain.exponentialRampToValueAtTime(gain, t0 + 0.01);
g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
osc.connect(g).connect(ctx.destination);
osc.start(t0);
osc.stop(t0 + dur + 0.02);
},
eat() { this.tone(660 + Math.random() * 140, 0.055, "triangle", 0.026, 120); },
poke() { this.tone(180, 0.09, "sawtooth", 0.022, -70); },
birth() { this.tone(520, 0.08, "sine", 0.028, 100); setTimeout(() => this.tone(760, 0.10, "triangle", 0.024, 80), 70); },
death() { this.tone(150, 0.22, "sine", 0.028, -70); },
fight() { this.tone(110, 0.055, "square", 0.018, 70); setTimeout(() => this.tone(90, 0.045, "sawtooth", 0.014, -40), 35); },
phase() { this.tone(440, 0.06, "sine", 0.018, 60); },
};
window.TarinaiAudio = audio;