66 lines
3.7 KiB
Bash
66 lines
3.7 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
# Synthesize the Observer Effect SFX with ffmpeg. Pure lavfi synthesis — no
|
||
|
|
# samples. Re-run to regenerate the .mp3s in this directory.
|
||
|
|
#
|
||
|
|
# nix-shell -p ffmpeg --run ./generate.sh
|
||
|
|
#
|
||
|
|
# Expressions are deliberately comma-free (logistic gates 1/(1+exp(-k*(t-T)))
|
||
|
|
# and exp() envelopes instead of between()/min()/max()) so nothing needs
|
||
|
|
# escaping inside the filtergraph.
|
||
|
|
set -euo pipefail
|
||
|
|
cd "$(dirname "$0")"
|
||
|
|
|
||
|
|
SR=48000
|
||
|
|
MP3=(-ac 1 -c:a libmp3lame -b:a 128k)
|
||
|
|
# Per-second clicks use WAV: mp3 adds a few ms of priming silence at the buffer
|
||
|
|
# start, which smears the transient against the digit changeover.
|
||
|
|
WAV=(-ac 1 -c:a pcm_s16le)
|
||
|
|
|
||
|
|
# --- pulse: a deep drum thump, a rising flute whistle, a metallic shimmer ----
|
||
|
|
PULSE='exp(-5*t)*0.9*sin(2*PI*(90*t-25*t*t))'
|
||
|
|
PULSE+=' + (1-exp(-25*t))*exp(-3*t)*0.16*sin(2*PI*(1500*t+850*t*t))'
|
||
|
|
PULSE+=' + exp(-2.2*t)*0.05*sin(2*PI*2700*t)*(1+0.5*sin(2*PI*7*t))'
|
||
|
|
ffmpeg -y -hide_banner -loglevel error \
|
||
|
|
-f lavfi -i "aevalsrc=${PULSE}:s=${SR}:d=1.4" \
|
||
|
|
-af "aecho=0.8:0.7:45|110:0.35|0.2,alimiter=limit=0.95,aresample=44100" \
|
||
|
|
"${MP3[@]}" pulse.mp3
|
||
|
|
|
||
|
|
# --- communion: rising roar + noise swell -> sub-bass impact -> screaming
|
||
|
|
# flutes over a beating void drone -------------------------------------
|
||
|
|
ROAR='(1-exp(-2.5*t))*exp(-0.28*t)*0.28*(sin(2*PI*(60*t-2.5*t*t))+0.5*sin(4*PI*(60*t-2.5*t*t))+0.33*sin(6*PI*(60*t-2.5*t*t)))'
|
||
|
|
IMPACT='(1/(1+exp(-30*(t-1.6))))*exp(-3.2*(t-1.6))*0.95*sin(2*PI*(80*(t-1.6)-35*(t-1.6)*(t-1.6)))'
|
||
|
|
FLUTES='(1/(1+exp(-30*(t-1.7))))*exp(-0.9*(t-1.7))*0.07*(sin(2*PI*1700*t)+sin(2*PI*2050*t)+sin(2*PI*2390*t)+sin(2*PI*2900*t)+sin(2*PI*3550*t))*(1+0.4*sin(2*PI*5*t))'
|
||
|
|
DRONE='(1/(1+exp(-25*(t-1.7))))*exp(-0.5*(t-1.7))*0.22*(sin(2*PI*40*t)+sin(2*PI*40.5*t))'
|
||
|
|
TONAL="${ROAR} + ${IMPACT} + ${FLUTES} + ${DRONE}"
|
||
|
|
SWELL="volume=volume='0.4*(1-exp(-2.2*t))*(1/(1+exp(8*(t-1.95))))*(0.6+0.4*sin(2*PI*0.9*t))':eval=frame"
|
||
|
|
ffmpeg -y -hide_banner -loglevel error \
|
||
|
|
-f lavfi -i "aevalsrc=${TONAL}:s=${SR}:d=6.5" \
|
||
|
|
-f lavfi -i "anoisesrc=color=white:amplitude=0.6:r=${SR}:d=6.5" \
|
||
|
|
-filter_complex "[1:a]highpass=f=3000,${SWELL}[n];[0:a][n]amix=inputs=2:normalize=0,aecho=0.8:0.85:55|150:0.35|0.2,alimiter=limit=0.96,aresample=44100[out]" \
|
||
|
|
-map "[out]" "${MP3[@]}" communion.mp3
|
||
|
|
|
||
|
|
# --- whisper: a brief eerie transmission for broadcast text ------------------
|
||
|
|
WHISP='(1-exp(-8*t))*exp(-2*t)*0.12*(sin(2*PI*620*t)+sin(2*PI*623*t))'
|
||
|
|
WHISP+=' + exp(-3*t)*0.04*sin(2*PI*(2200*t-300*t*t))'
|
||
|
|
WHISP+=' + 0.03*exp(-1.5*t)*sin(2*PI*1240*t)*(1+0.6*sin(2*PI*11*t))'
|
||
|
|
ffmpeg -y -hide_banner -loglevel error \
|
||
|
|
-f lavfi -i "aevalsrc=${WHISP}:s=${SR}:d=1.6" \
|
||
|
|
-f lavfi -i "anoisesrc=color=white:amplitude=0.25:r=${SR}:d=1.6" \
|
||
|
|
-filter_complex "[1:a]bandpass=f=1500:width_type=h:w=900,volume=volume='0.18*exp(-2.5*t)*(0.5+0.5*sin(2*PI*13*t))':eval=frame[n];[0:a][n]amix=inputs=2:normalize=0,volume=12dB,aecho=0.8:0.6:70:0.3,alimiter=limit=0.9,aresample=44100[out]" \
|
||
|
|
-map "[out]" "${MP3[@]}" whisper.mp3
|
||
|
|
|
||
|
|
# --- relay: a dry electromechanical seconds step (armature snap + body) -------
|
||
|
|
# The seconds advance is a stepping relay, not a pendulum: a sharp broadband
|
||
|
|
# snap with a faint low "thunk" and almost no tonal tail.
|
||
|
|
RELAY='exp(-120*t)*0.3*sin(2*PI*430*t) + exp(-220*t)*0.32*sin(2*PI*1700*t)'
|
||
|
|
ffmpeg -y -hide_banner -loglevel error \
|
||
|
|
-f lavfi -i "aevalsrc=${RELAY}:s=${SR}:d=0.09" \
|
||
|
|
-f lavfi -i "anoisesrc=color=white:amplitude=0.6:r=${SR}:d=0.09" \
|
||
|
|
-filter_complex "[1:a]bandpass=f=2400:width_type=h:w=2400,volume=volume='0.6*exp(-230*t)':eval=frame[n];[0:a][n]amix=inputs=2:normalize=0,alimiter=limit=0.95,aresample=44100[out]" \
|
||
|
|
-map "[out]" "${WAV[@]}" relay.wav
|
||
|
|
|
||
|
|
echo "generated:"
|
||
|
|
for f in pulse.mp3 communion.mp3 whisper.mp3 relay.wav; do
|
||
|
|
printf ' %-14s %s\n' "$f" "$(du -h "$f" | cut -f1)"
|
||
|
|
done
|