export class Sound { constructor() { this.ctx = null; this.engineOsc = null; this.engineGain = null; this.initialized = false; } init() { if (this.initialized) return; try { this.ctx = new (window.AudioContext || window.webkitAudioContext)(); this.engineOsc = this.ctx.createOscillator(); this.engineOsc.type = 'sawtooth'; this.engineGain = this.ctx.createGain(); this.engineGain.gain.value = 0; // Low-pass filter for muffled engine sound this.engineFilter = this.ctx.createBiquadFilter(); this.engineFilter.type = 'lowpass'; this.engineFilter.frequency.value = 300; this.engineOsc.connect(this.engineFilter); this.engineFilter.connect(this.engineGain); this.engineGain.connect(this.ctx.destination); this.engineOsc.start(); this.initialized = true; } catch { // Audio not available, silently fail } } update(throttle) { if (!this.initialized || !this.engineOsc) return; const baseFreq = 60; const maxFreq = 180; this.engineOsc.frequency.value = baseFreq + throttle * (maxFreq - baseFreq); this.engineGain.gain.value = Math.min(throttle * 0.08, 0.08); this.engineFilter.frequency.value = 200 + throttle * 200; } beep(frequency = 800, duration = 80) { if (!this.initialized) return; try { const osc = this.ctx.createOscillator(); const gain = this.ctx.createGain(); osc.type = 'square'; osc.frequency.value = frequency; gain.gain.value = 0.05; osc.connect(gain); gain.connect(this.ctx.destination); osc.start(); osc.stop(this.ctx.currentTime + duration / 1000); } catch { // Ignore audio errors } } takeoffSound() { if (!this.initialized) return; try { const osc = this.ctx.createOscillator(); const gain = this.ctx.createGain(); osc.type = 'triangle'; osc.frequency.value = 400; osc.frequency.linearRampToValueAtTime(800, this.ctx.currentTime + 0.3); gain.gain.value = 0.08; gain.gain.linearRampToValueAtTime(0, this.ctx.currentTime + 0.5); osc.connect(gain); gain.connect(this.ctx.destination); osc.start(); osc.stop(this.ctx.currentTime + 0.5); } catch { // Ignore } } }