최초 커밋

This commit is contained in:
KIM SU BO committed 2026-10-05 19:47:55 +09:00
1 parent a28d331e3e
commit 618ba02cb9
124 files changed
+49297 -2

No files matched your search

+141
View File
@@ -0,0 +1,141 @@
/**
* Synthesizer for Drone Rotor Hum and Tactical HUD Audio FX
* Uses Web Audio API without external audio file dependencies
*/
class DroneAudioSystem {
constructor() {
this.ctx = null;
this.enabled = false;
this.leaderGain = null;
this.followerGain = null;
this.leaderOsc1 = null;
this.leaderOsc2 = null;
this.followerOsc1 = null;
this.followerOsc2 = null;
}
init() {
if (this.ctx) return;
try {
const AudioContext = window.AudioContext || window.webkitAudioContext;
this.ctx = new AudioContext();
// Master compressor to prevent clipping
const compressor = this.ctx.createDynamicsCompressor();
compressor.threshold.setValueAtTime(-18, this.ctx.currentTime);
compressor.knee.setValueAtTime(30, this.ctx.currentTime);
compressor.ratio.setValueAtTime(12, this.ctx.currentTime);
compressor.attack.setValueAtTime(0.003, this.ctx.currentTime);
compressor.release.setValueAtTime(0.25, this.ctx.currentTime);
compressor.connect(this.ctx.destination);
this.masterGain = this.ctx.createGain();
this.masterGain.gain.setValueAtTime(0.2, this.ctx.currentTime);
this.masterGain.connect(compressor);
// Leader Drone sound synthesizer (Dual harmonic oscillator + lowpass filter)
this.leaderFilter = this.ctx.createBiquadFilter();
this.leaderFilter.type = "lowpass";
this.leaderFilter.frequency.setValueAtTime(650, this.ctx.currentTime);
this.leaderGain = this.ctx.createGain();
this.leaderGain.gain.setValueAtTime(0.0, this.ctx.currentTime);
this.leaderGain.connect(this.leaderFilter);
this.leaderFilter.connect(this.masterGain);
this.leaderOsc1 = this.ctx.createOscillator();
this.leaderOsc1.type = "sawtooth";
this.leaderOsc1.frequency.setValueAtTime(95, this.ctx.currentTime);
this.leaderOsc2 = this.ctx.createOscillator();
this.leaderOsc2.type = "triangle";
this.leaderOsc2.frequency.setValueAtTime(190, this.ctx.currentTime);
this.leaderOsc1.connect(this.leaderGain);
this.leaderOsc2.connect(this.leaderGain);
this.leaderOsc1.start();
this.leaderOsc2.start();
// Follower Drone sound synthesizer
this.followerFilter = this.ctx.createBiquadFilter();
this.followerFilter.type = "lowpass";
this.followerFilter.frequency.setValueAtTime(600, this.ctx.currentTime);
this.followerGain = this.ctx.createGain();
this.followerGain.gain.setValueAtTime(0.0, this.ctx.currentTime);
this.followerGain.connect(this.followerFilter);
this.followerFilter.connect(this.masterGain);
this.followerOsc1 = this.ctx.createOscillator();
this.followerOsc1.type = "sawtooth";
this.followerOsc1.frequency.setValueAtTime(98, this.ctx.currentTime);
this.followerOsc2 = this.ctx.createOscillator();
this.followerOsc2.type = "triangle";
this.followerOsc2.frequency.setValueAtTime(196, this.ctx.currentTime);
this.followerOsc1.connect(this.followerGain);
this.followerOsc2.connect(this.followerGain);
this.followerOsc1.start();
this.followerOsc2.start();
this.enabled = true;
} catch (e) {
console.warn("Web Audio not supported or blocked", e);
}
}
toggle() {
if (!this.ctx) {
this.init();
return true;
}
if (this.ctx.state === 'suspended') {
this.ctx.resume();
this.enabled = true;
return true;
}
this.enabled = !this.enabled;
if (!this.enabled && this.leaderGain && this.followerGain) {
this.leaderGain.gain.setValueAtTime(0.0, this.ctx.currentTime);
this.followerGain.gain.setValueAtTime(0.0, this.ctx.currentTime);
}
return this.enabled;
}
updateMotors(leaderRpm, followerRpm, speedLeader, speedFollower) {
if (!this.enabled || !this.ctx || this.ctx.state !== 'running') return;
const now = this.ctx.currentTime;
// Pitch proportional to RPM and velocity
const baseFreq1 = 85 + (speedLeader * 2.5) + ((leaderRpm - 4500) * 0.015);
const baseFreq2 = 88 + (speedFollower * 2.5) + ((followerRpm - 4500) * 0.015);
this.leaderOsc1.frequency.setTargetAtTime(Math.max(60, Math.min(220, baseFreq1)), now, 0.08);
this.leaderOsc2.frequency.setTargetAtTime(Math.max(120, Math.min(440, baseFreq1 * 2)), now, 0.08);
this.followerOsc1.frequency.setTargetAtTime(Math.max(60, Math.min(220, baseFreq2)), now, 0.08);
this.followerOsc2.frequency.setTargetAtTime(Math.max(120, Math.min(440, baseFreq2 * 2)), now, 0.08);
this.leaderGain.gain.setTargetAtTime(0.08, now, 0.1);
this.followerGain.gain.setTargetAtTime(0.08, now, 0.1);
}
playBeep(freq = 880, duration = 0.08) {
if (!this.enabled || !this.ctx) return;
try {
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = "sine";
osc.frequency.setValueAtTime(freq, this.ctx.currentTime);
gain.gain.setValueAtTime(0.08, this.ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, this.ctx.currentTime + duration);
osc.connect(gain);
gain.connect(this.masterGain);
osc.start();
osc.stop(this.ctx.currentTime + duration);
} catch (e) {}
}
}
window.droneAudio = new DroneAudioSystem();