"use strict"; const ShieldExplosionSound = (function(){ return function(audio_ctx, output_node, volume_multiplier = 1.0){ const now = audio_ctx.currentTime; const duration = 1.6; // Explosión profunda y larga const preset_bus = audio_ctx.createGain(); preset_bus.gain.setValueAtTime(volume_multiplier, now); // 1. RUIDO BROWNIANO / ROJO (El trueno profundo) // Genera una acumulación continua de bajas frecuencias (cero agudos) const buffer_size = Math.floor(audio_ctx.sampleRate * duration); const buffer = audio_ctx.createBuffer(1, buffer_size, audio_ctx.sampleRate); const data = buffer.getChannelData(0); let last_val = 0.0; for(let i = 0; i < buffer_size; i++){ const white = (Math.random() * 2) - 1; // Integración para convertir ruido blanco en browniano (grave puro) last_val = (last_val + (0.02 * white)) / 1.02; data[i] = last_val * 3.5; } const rumble_source = audio_ctx.createBufferSource(); rumble_source.buffer = buffer; // Filtro paso-bajo muy estricto: solo deja pasar el retumbe sordo const rumble_filter = audio_ctx.createBiquadFilter(); rumble_filter.type = "lowpass"; rumble_filter.frequency.setValueAtTime(140, now); rumble_filter.frequency.exponentialRampToValueAtTime(30, now + duration); const rumble_gain = audio_ctx.createGain(); rumble_gain.gain.setValueAtTime(1.2, now); // El estruendo decae lentamente como una onda expansiva real rumble_gain.gain.exponentialRampToValueAtTime(0.001, now + duration); rumble_source.connect(rumble_filter); rumble_filter.connect(rumble_gain); rumble_gain.connect(preset_bus); rumble_source.start(now); rumble_source.stop(now + duration); // 2. ONDA DE CHOQUE SUBGRAVE (El golpe sísmico en el pecho) // Desciende a frecuencias casi inaudibles pero que mueven la membrana de los cascos const sub_osc = audio_ctx.createOscillator(); const sub_gain = audio_ctx.createGain(); sub_osc.type = "sine"; sub_osc.frequency.setValueAtTime(68, now); sub_osc.frequency.exponentialRampToValueAtTime(18, now + 0.85); sub_gain.gain.setValueAtTime(1.4, now); sub_gain.gain.exponentialRampToValueAtTime(0.001, now + 0.85); sub_osc.connect(sub_gain); sub_gain.connect(preset_bus); sub_osc.start(now); sub_osc.stop(now + 0.85); // 3. SEGUNDO OSCILADOR DE GRUÑIDO (Rumble modulado) // Aporta fricción y textura de metal/masa estallando en el vacío const rumble_osc = audio_ctx.createOscillator(); const rumble_osc_gain = audio_ctx.createGain(); rumble_osc.type = "triangle"; rumble_osc.frequency.setValueAtTime(45, now); rumble_osc.frequency.exponentialRampToValueAtTime(12, now + 1.2); rumble_osc_gain.gain.setValueAtTime(0.7, now); rumble_osc_gain.gain.exponentialRampToValueAtTime(0.001, now + 1.2); rumble_osc.connect(rumble_osc_gain); rumble_osc_gain.connect(preset_bus); rumble_osc.start(now); rumble_osc.stop(now + 1.2); // Salida directa hacia el bus maestro y limitador preset_bus.connect(output_node); }; })();