94 lines
3.6 KiB
JavaScript
94 lines
3.6 KiB
JavaScript
"use strict";
|
|
|
|
const ShieldLowAlarmSound = (function(){
|
|
|
|
return function(audio_ctx, output_node, volume_multiplier = 1.0){
|
|
const now = audio_ctx.currentTime;
|
|
const duration = 0.22; // Pulso de sirena industrial rápido
|
|
|
|
const preset_bus = audio_ctx.createGain();
|
|
preset_bus.gain.setValueAtTime(volume_multiplier * 0.45, now);
|
|
|
|
// 1. REVERBERACIÓN CORTA DE MAMPARA (Eco de pasillo de nave)
|
|
const room_delay = audio_ctx.createDelay();
|
|
room_delay.delayTime.setValueAtTime(0.045, now);
|
|
|
|
const room_feedback = audio_ctx.createGain();
|
|
room_feedback.gain.setValueAtTime(0.35, now);
|
|
|
|
const room_filter = audio_ctx.createBiquadFilter();
|
|
room_filter.type = "lowpass";
|
|
room_filter.frequency.setValueAtTime(1200, now);
|
|
|
|
room_delay.connect(room_filter);
|
|
room_filter.connect(room_feedback);
|
|
room_feedback.connect(room_delay);
|
|
room_filter.connect(preset_bus);
|
|
|
|
// 2. ENVOLVENTE DINÁMICA DE BOCINA (Ataque percusivo + cuerpo)
|
|
const env = audio_ctx.createGain();
|
|
env.gain.setValueAtTime(0.001, now);
|
|
// Ataque rápido simulando apertura de válvula
|
|
env.gain.linearRampToValueAtTime(0.85, now + 0.02);
|
|
env.gain.setValueAtTime(0.85, now + 0.14);
|
|
env.gain.exponentialRampToValueAtTime(0.001, now + duration);
|
|
|
|
// 3. GENERADOR DE SIRENA INDUSTRIAL (Síntesis FM limpia)
|
|
// Transportadora principal: Tono puro de bocina (senoidal limpia)
|
|
const carrier = audio_ctx.createOscillator();
|
|
carrier.type = "sine";
|
|
|
|
// Barrido característico de evacuación: empieza arriba y resbala hacia abajo
|
|
carrier.frequency.setValueAtTime(740, now);
|
|
carrier.frequency.exponentialRampToValueAtTime(460, now + duration);
|
|
|
|
// Modulador FM: Añade el zumbido de vibración de la membrana metálica
|
|
const modulator = audio_ctx.createOscillator();
|
|
const mod_gain = audio_ctx.createGain();
|
|
|
|
modulator.type = "sine";
|
|
// Modulación a 45Hz para simular el traqueteo del motor de la sirena
|
|
modulator.frequency.setValueAtTime(45, now);
|
|
mod_gain.gain.setValueAtTime(140, now);
|
|
mod_gain.gain.exponentialRampToValueAtTime(40, now + duration);
|
|
|
|
modulator.connect(mod_gain);
|
|
mod_gain.connect(carrier.frequency);
|
|
|
|
// 4. SEGUNDO TONO ARMÓNICO (Cuerpo de resonador hueco)
|
|
const sub_carrier = audio_ctx.createOscillator();
|
|
const sub_gain = audio_ctx.createGain();
|
|
sub_carrier.type = "triangle";
|
|
sub_carrier.frequency.setValueAtTime(370, now);
|
|
sub_carrier.frequency.exponentialRampToValueAtTime(230, now + duration);
|
|
|
|
sub_gain.gain.setValueAtTime(0.3, now);
|
|
sub_carrier.connect(sub_gain);
|
|
sub_gain.connect(env);
|
|
|
|
// 5. FILTRO PASO-BANDA (Emula el megáfono físico de cabina)
|
|
const megaphone_filter = audio_ctx.createBiquadFilter();
|
|
megaphone_filter.type = "bandpass";
|
|
megaphone_filter.frequency.setValueAtTime(650, now);
|
|
megaphone_filter.Q.setValueAtTime(1.8, now);
|
|
|
|
carrier.connect(env);
|
|
env.connect(megaphone_filter);
|
|
|
|
// Enviamos al bus directo y una copia a la reverberación de mampara
|
|
megaphone_filter.connect(preset_bus);
|
|
megaphone_filter.connect(room_delay);
|
|
|
|
preset_bus.connect(output_node);
|
|
|
|
// Disparo coordinado
|
|
modulator.start(now);
|
|
carrier.start(now);
|
|
sub_carrier.start(now);
|
|
|
|
modulator.stop(now + duration);
|
|
carrier.stop(now + duration);
|
|
sub_carrier.stop(now + duration);
|
|
};
|
|
|
|
})(); |