// @name Emergency Call: Radio Spectrum Waterfall
// @group Composition
// @param uOffsetX "Screen X Position" 0.0 -1.25 1.25 0.01
// @param uOffsetY "Screen Y Position" 0.0 -0.8 0.8 0.01
// @param uScale "Monitor Scale" 1.0 0.4 2.0 0.01
// @param uAspect "Monitor Aspect" 1.45 0.7 2.3 0.01
// @group Spectrum
// @param uGain "Receiver Gain" 1.45 0.0 4.0 0.01
// @param uBands "Frequency Bins" 48.0 12.0 96.0 1.0
// @param uCarrierFrequency "Carrier Position" 0.58 0.05 0.95 0.005
// @param uCarrierWidth "Carrier Width" 0.018 0.003 0.08 0.001
// @param uVoiceBandwidth "Voice Bandwidth" 0.22 0.05 0.55 0.01
// @param uNoiseFloor "Radio Noise Floor" 0.12 0.0 0.6 0.01
// @param uPeakHold "Peak Hold" 0.7 0.0 1.0 0.01
// @param uSpectrumHeight "Spectrum Height" 0.24 0.08 0.4 0.01
// @group Waterfall
// @param uHistorySpeed "History Scroll Speed" 0.28 -1.5 1.5 0.01
// @param uHistoryContrast "History Contrast" 1.4 0.2 4.0 0.01
// @param uPersistence "Transmission Persistence" 0.64 0.0 1.0 0.01
// @param uDropouts "Signal Dropouts" 0.12 0.0 0.8 0.01
// @param uScanWidth "Scanner Line Width" 0.006 0.001 0.04 0.001
// @param uGridDensity "Grid Density" 10.0 4.0 24.0 1.0
// @group Color
// @param uColdHue "Noise Hue" 0.62 0.0 1.0 0.005
// @param uVoiceHue "Voice Hue" 0.47 0.0 1.0 0.005
// @param uHotHue "Hot Signal Hue" 0.04 0.0 1.0 0.005
// @param uSaturation "Saturation" 0.82 0.0 1.0 0.01
// @param uGlow "Signal Glow" 0.9 0.0 3.0 0.01
// @param uBackground "Background Level" 0.025 0.0 0.25 0.005
// @param uExposure "Exposure" 1.15 0.2 3.0 0.01
// @group Radio Motion
// @param uTuningDrift "Tuning Drift" 0.035 0.0 0.2 0.005
// @param uPulseSpeed "Transmission Pulse" 1.1 0.0 4.0 0.01
// @param uAudioReact "Audio Reactivity" 1.0 0.0 2.0 0.01

vec3 H(vec3 c){vec3 p=abs(fract(c.xxx+vec3(0,.666667,.333333))*6.-3.);return c.z*mix(vec3(1),clamp(p-1.,0.,1.),c.y);}
float hash(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}
float spectrum(float x,float time){
    float carrier=uCarrierFrequency+sin(time*.17)*uTuningDrift,voice=exp(-pow((x-carrier)/max(uVoiceBandwidth,.01),2.));
    float formants=exp(-pow((x-carrier+.09)/.035,2.))*iMid+exp(-pow((x-carrier-.07)/.024,2.))*iTreble+exp(-pow((x-carrier+.17)/.06,2.))*iBass;
    float bins=max(12.,floor(uBands+.5)),noise=hash(vec2(floor(x*bins),floor(time*9.)))*uNoiseFloor;
    return (voice*(iVolume*.62+iMid*.42)+formants*.55)*uGain*uAudioReact+noise;
}
void mainImage(out vec4 o,in vec2 f){
    vec2 q=(f-.5*iResolution.xy)/iResolution.y,p=(q-vec2(uOffsetX,uOffsetY))/max(uScale,.001);p.x/=uAspect;vec2 uv=p/vec2(.52,.42)*.5+.5;float inside=step(0.,uv.x)*step(uv.x,1.)*step(0.,uv.y)*step(uv.y,1.);vec3 cold=H(vec3(uColdHue,uSaturation,1.)),voice=H(vec3(uVoiceHue,uSaturation,1.)),hot=H(vec3(uHotHue,.92,1.));vec3 col=cold*uBackground;
    float waterfall=0.;if(uv.y>.25){float history=(uv.y-.25)/.75,time=iTime-history/max(abs(uHistorySpeed),.02);float sig=spectrum(uv.x,time);float dropout=step(uDropouts,hash(vec2(floor(time*5.),floor(uv.x*9.))));float ridges=.45+.55*sin((uv.x-uCarrierFrequency)*240.+time*17.);waterfall=pow(max(sig*ridges*dropout,0.),uHistoryContrast)*mix(.4,1.,uPersistence);}
    vec3 wf=mix(cold,voice,smoothstep(.08,.45,waterfall));wf=mix(wf,hot,smoothstep(.48,1.1,waterfall));col+=wf*waterfall*uGlow;
    float sig=spectrum(uv.x,iTime),base=.12,height=clamp(sig,0.,1.)*uSpectrumHeight;float fill=step(base-.01,uv.y)*step(uv.y,base+height),edge=exp(-abs(uv.y-(base+height))/max(uScanWidth,.001));col+=mix(voice,hot,smoothstep(.55,1.,sig))*(fill*.28+edge*1.4);
    float grid=(1.-smoothstep(.003,.007,min(abs(fract(uv.x*uGridDensity)-.5),abs(fract(uv.y*uGridDensity)-.5))))*.1;col+=cold*grid*inside;float carrier=exp(-abs(uv.x-uCarrierFrequency-sin(iTime*.17)*uTuningDrift)/max(uCarrierWidth,.001));col+=hot*carrier*(.12+iVolume*uAudioReact*.4);
    float scan=exp(-abs(uv.y-fract(iTime*uHistorySpeed))/max(uScanWidth,.001));col+=voice*scan*.35;col*=inside*uExposure;col=col/(1.+col*.5);o=vec4(pow(max(col,0.),vec3(.84)),1.);
}
