"use strict"; const MothershipImpactSound = (function(){ // Curva de saturación analógica para darle máxima pegada sin romper el audio const make_distortion_curve = (amount = 20) => { const samples = 44100; const curve = new Float32Array(samples); const deg = Math.PI / 180; for(let i = 0; i < samples; ++i){ const x = (i * 2) / samples - 1; curve[i] = ((3 + amount) * x * 20 * deg) / (Math.PI + amount * Math.abs(x)); } return curve; }; return function(audio_ctx, output_node, volume_multiplier = 1.0){ const now = audio_ctx.currentTime; const duration = 2.4; // Retumbe largo y cavernoso // Bus maestro del efecto con booster de presencia const master_bus = audio_ctx.createGain(); master_bus.gain.setValueAtTime(volume_multiplier * 1.35, now); // Saturador suave para sonido "aplastante" const shaper = audio_ctx.createWaveShaper(); shaper.curve = make_distortion_curve(18); shaper.oversample = "2x"; // ======================================================== // CAPA 1: SUB-GRAVE SÍSMICO (Terremoto acústico en cabina) // ======================================================== const sub_osc = audio_ctx.createOscillator(); const sub_gain = audio_ctx.createGain(); sub_osc.type = "sine"; sub_osc.frequency.setValueAtTime(145, now); // Caída drástica a frecuencias casi inaudibles pero que mueven aire sub_osc.frequency.exponentialRampToValueAtTime(22, now + 0.45); sub_osc.frequency.setValueAtTime(20, now + duration); sub_gain.gain.setValueAtTime(0.001, now); sub_gain.gain.linearRampToValueAtTime(1.0, now + 0.015); sub_gain.gain.exponentialRampToValueAtTime(0.001, now + 1.8); sub_osc.connect(sub_gain); sub_gain.connect(shaper); // ======================================================== // CAPA 2: CRUJIDO DE CASCO Y METRALLA (Ráfaga de ruido blanco) // ======================================================== const buffer_size = Math.floor(audio_ctx.sampleRate * 1.5); const noise_buffer = audio_ctx.createBuffer(1, buffer_size, audio_ctx.sampleRate); const noise_data = noise_buffer.getChannelData(0); for(let i = 0; i < buffer_size; i++){ noise_data[i] = (Math.random() * 2 - 1); } const noise_src = audio_ctx.createBufferSource(); noise_src.buffer = noise_buffer; const noise_filter = audio_ctx.createBiquadFilter(); noise_filter.type = "lowpass"; noise_filter.frequency.setValueAtTime(4200, now); noise_filter.frequency.exponentialRampToValueAtTime(110, now + 1.2); noise_filter.Q.setValueAtTime(2.5, now); const noise_env = audio_ctx.createGain(); noise_env.gain.setValueAtTime(0.001, now); noise_env.gain.linearRampToValueAtTime(0.9, now + 0.02); noise_env.gain.exponentialRampToValueAtTime(0.001, now + 1.4); noise_src.connect(noise_filter); noise_filter.connect(noise_env); noise_env.connect(shaper); // ======================================================== // CAPA 3: DEFORMACIÓN METÁLICA (Resonador desafinado) // ======================================================== const metal_osc_a = audio_ctx.createOscillator(); const metal_osc_b = audio_ctx.createOscillator(); const metal_gain = audio_ctx.createGain(); metal_osc_a.type = "sawtooth"; metal_osc_b.type = "triangle"; metal_osc_a.frequency.setValueAtTime(78, now); metal_osc_a.frequency.exponentialRampToValueAtTime(32, now + 0.9); metal_osc_b.frequency.setValueAtTime(114, now); metal_osc_b.frequency.exponentialRampToValueAtTime(38, now + 0.9); metal_gain.gain.setValueAtTime(0.001, now); metal_gain.gain.linearRampToValueAtTime(0.55, now + 0.04); metal_gain.gain.exponentialRampToValueAtTime(0.001, now + 1.1); metal_osc_a.connect(metal_gain); metal_osc_b.connect(metal_gain); metal_gain.connect(shaper); // ======================================================== // CAPA 4: COLA DE REVERBERACIÓN DE LA NAVE (Eco retumbante) // ======================================================== const rumble_delay = audio_ctx.createDelay(); rumble_delay.delayTime.setValueAtTime(0.075, now); const rumble_fb = audio_ctx.createGain(); rumble_fb.gain.setValueAtTime(0.48, now); const rumble_filter = audio_ctx.createBiquadFilter(); rumble_filter.type = "lowpass"; rumble_filter.frequency.setValueAtTime(320, now); rumble_delay.connect(rumble_filter); rumble_filter.connect(rumble_fb); rumble_fb.connect(rumble_delay); rumble_filter.connect(master_bus); // Conexiones de salida shaper.connect(master_bus); shaper.connect(rumble_delay); master_bus.connect(output_node); // Disparo coordinado de los generadores sub_osc.start(now); noise_src.start(now); metal_osc_a.start(now); metal_osc_b.start(now); sub_osc.stop(now + duration); noise_src.stop(now + 1.5); metal_osc_a.stop(now + 1.2); metal_osc_b.stop(now + 1.2); }; })();