update
This commit is contained in:
1 parent
83278b2998
commit
b38ceec784
128 files changed
+51554
No files matched your search
@@ -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();
|
||||
Reference in new issue
Block a user