Spaces:
Sleeping
Sleeping
| """The bad-finale horror sting, synthesized at import time (~50 ms): a | |
| dissonant descending shriek over a sub rumble with noise stabs. ~3.2 s mono | |
| WAV. Generated in code so no binary lives in the repo (HF rejects raw .wav). | |
| Tune and listen: `python sting.py` writes sting_preview.wav.""" | |
| import io | |
| import wave | |
| import numpy as np | |
| _SR = 22050 | |
| _DUR = 3.2 | |
| def sting_wav_bytes() -> bytes: | |
| t = np.linspace(0, _DUR, int(_SR * _DUR), endpoint=False) | |
| rng = np.random.default_rng(13) | |
| # 1. the shriek: inharmonic partials sweeping down with deepening vibrato | |
| sweep = 2200 * np.exp(-1.1 * t) + 380 | |
| vibrato = 1 + 0.025 * np.sin(2 * np.pi * (5 + 3 * t) * t) | |
| phase = 2 * np.pi * np.cumsum(sweep * vibrato) / _SR | |
| shriek = (np.sin(phase) + 0.6 * np.sin(1.93 * phase) | |
| + 0.4 * np.sin(2.71 * phase)) | |
| shriek *= np.clip(t / 0.35, 0, 1) # swell in | |
| shriek *= np.exp(-0.55 * t) # decay | |
| # 2. sub rumble: slow 42-55 Hz wobble underneath everything | |
| wob = 48 + 7 * np.sin(2 * np.pi * 0.7 * t) | |
| rumble = np.sin(2 * np.pi * np.cumsum(wob) / _SR) | |
| rumble *= np.clip(t / 0.8, 0, 1) * 0.9 | |
| # 3. noise stabs at the start and mid-convulsion | |
| noise = rng.standard_normal(len(t)) | |
| stab = np.zeros_like(t) | |
| for start, decay in ((0.0, 18.0), (1.5, 22.0)): | |
| env = np.where(t >= start, np.exp(-decay * (t - start)), 0) | |
| stab += env | |
| mix = 0.9 * shriek + 0.8 * rumble + 0.5 * noise * stab | |
| mix = np.tanh(1.6 * mix) # gentle distortion, glues layers | |
| fade = int(0.5 * _SR) | |
| mix[-fade:] *= np.linspace(1, 0, fade) # fade out | |
| mix = mix / np.max(np.abs(mix)) * 0.6 # moderate volume, no blasting | |
| buf = io.BytesIO() | |
| with wave.open(buf, "wb") as w: | |
| w.setnchannels(1) | |
| w.setsampwidth(2) | |
| w.setframerate(_SR) | |
| w.writeframes((mix * 32767).astype(np.int16).tobytes()) | |
| return buf.getvalue() | |
| def _wav(mix: np.ndarray, sr: int) -> bytes: | |
| buf = io.BytesIO() | |
| with wave.open(buf, "wb") as w: | |
| w.setnchannels(1) | |
| w.setsampwidth(2) | |
| w.setframerate(sr) | |
| w.writeframes((mix * 32767).astype(np.int16).tobytes()) | |
| return buf.getvalue() | |
| # Hollow's reply chime: a single detuned music-box note, one per turn, | |
| # rotating through a small minor-feel set so it never becomes a loop. | |
| _CHIME_FREQS = (659.3, 493.9, 784.0) # E5, B4, G5 | |
| def chime_wav_bytes(idx: int = 0) -> bytes: | |
| sr = 22050 | |
| dur = 0.8 | |
| t = np.linspace(0, dur, int(sr * dur), endpoint=False) | |
| f = _CHIME_FREQS[idx % len(_CHIME_FREQS)] | |
| def tine(freq): | |
| # struck-tine partials, slightly inharmonic, fast decay | |
| return (np.sin(2 * np.pi * freq * t) | |
| + 0.35 * np.sin(2 * np.pi * 2.76 * freq * t) | |
| + 0.12 * np.sin(2 * np.pi * 5.4 * freq * t)) | |
| # two oscillators a breath apart — the music box is out of tune | |
| note = tine(f) + tine(f * 1.006) | |
| note *= np.exp(-5.5 * t) # tine decay | |
| note *= np.clip(t / 0.004, 0, 1) # 4 ms attack, no click | |
| note = note / np.max(np.abs(note)) * 0.22 # quiet — plays every turn | |
| return _wav(note, sr) | |
| def _thud(t, center, freq, amp, width): | |
| """A single soft low drum hit centered at `center` seconds.""" | |
| env = np.exp(-((t - center) ** 2) / (2 * width ** 2)) | |
| return amp * np.sin(2 * np.pi * freq * t) * env | |
| def heartbeat_wav_bytes() -> bytes: | |
| """A loopable lub-dub heartbeat bed for the finale build. Exactly two | |
| beats (1.7 s) so the HTML `loop` attribute repeats seamlessly.""" | |
| sr = 22050 | |
| beat = 0.85 # ~70 bpm | |
| beats = 2 | |
| dur = beat * beats | |
| t = np.linspace(0, dur, int(sr * dur), endpoint=False) | |
| sig = np.zeros_like(t) | |
| for b in range(beats): | |
| b0 = b * beat | |
| sig += _thud(t, b0 + 0.00, 60, 1.0, 0.035) # lub | |
| sig += _thud(t, b0 + 0.16, 46, 0.7, 0.045) # dub | |
| sig = np.tanh(1.5 * sig) | |
| sig = sig / np.max(np.abs(sig)) * 0.45 | |
| return _wav(sig, sr) | |
| def sigh_wav_bytes() -> bytes: | |
| """Relief for the redemption climax: a soft filtered exhale over a warm | |
| major drone that swells and resolves. ~2.6 s.""" | |
| sr = 22050 | |
| dur = 2.6 | |
| t = np.linspace(0, dur, int(sr * dur), endpoint=False) | |
| rng = np.random.default_rng(7) | |
| # breath: smoothed noise (a soft 'hhh') with a quick-in long-out envelope | |
| noise = rng.standard_normal(len(t)) | |
| kernel = np.hanning(220) | |
| kernel /= kernel.sum() | |
| breath = np.convolve(noise, kernel, mode="same") | |
| exhale = np.clip(t / 0.15, 0, 1) * np.exp(-1.6 * t) | |
| breath *= exhale * 0.5 | |
| # warm resolving tone: G3 + major third + fifth, swelling then fading | |
| tone = (np.sin(2 * np.pi * 196.00 * t) | |
| + 0.5 * np.sin(2 * np.pi * 246.94 * t) | |
| + 0.4 * np.sin(2 * np.pi * 293.66 * t)) | |
| swell = np.sin(np.pi * np.clip(t / dur, 0, 1)) | |
| tone *= swell * 0.25 | |
| mix = breath + tone | |
| fade = int(0.3 * sr) | |
| mix[-fade:] *= np.linspace(1, 0, fade) | |
| mix = mix / np.max(np.abs(mix)) * 0.5 | |
| return _wav(mix, sr) | |
| def flatline_wav_bytes() -> bytes: | |
| """The visitor-loop climax: three decelerating beats, then the line goes | |
| flat — a sustained low tone that fades. Your heart stops being yours. ~4 s.""" | |
| sr = 22050 | |
| dur = 4.0 | |
| t = np.linspace(0, dur, int(sr * dur), endpoint=False) | |
| sig = np.zeros_like(t) | |
| for i, c in enumerate((0.0, 0.95, 2.1)): # widening gaps | |
| amp = 1.0 - 0.25 * i | |
| sig += _thud(t, c, 58, amp, 0.035) | |
| sig += _thud(t, c + 0.16, 46, 0.7 * amp, 0.045) | |
| flat_start = 2.5 | |
| flat = np.where(t >= flat_start, 0.18 * np.sin(2 * np.pi * 55 * t), 0.0) | |
| flat *= np.clip((t - flat_start) / 0.2, 0, 1) | |
| sig += flat | |
| fade = int(0.6 * sr) | |
| sig[-fade:] *= np.linspace(1, 0, fade) | |
| sig = sig / np.max(np.abs(sig)) * 0.5 | |
| return _wav(sig, sr) | |
| def menu_loop_wav_bytes() -> bytes: | |
| """Deep dread menu bed, made audible: a dark hollow-fifth body in the | |
| low-mids (heard on any speaker) over a felt sub, with a slow dissonant | |
| swell and a distant toll. Seamless loop — every sustained component is | |
| integer-cycle over the 16 s loop, so no fade-dip is needed at the seam.""" | |
| sr = 22050 | |
| dur = 16.0 | |
| t = np.linspace(0, dur, int(sr * dur), endpoint=False) | |
| # slow breathing, exactly 2 cycles over the loop (continuous at the seam) | |
| breath = 0.8 + 0.2 * np.sin(2 * np.pi * (2.0 / dur) * t) | |
| # 1. sub (felt): 41 + 27.5 Hz (41*16, 27.5*16 are integers) | |
| sub = (np.sin(2 * np.pi * 41.0 * t) | |
| + 0.6 * np.sin(2 * np.pi * 27.5 * t)) * 0.16 * breath | |
| # 2. audible body: a hollow fifth in the low-mids (65.5/98/131 Hz + harmonics) | |
| def tone(f): | |
| return (np.sin(2 * np.pi * f * t) | |
| + 0.4 * np.sin(2 * np.pi * 2 * f * t) | |
| + 0.18 * np.sin(2 * np.pi * 3 * f * t)) | |
| body = (tone(65.5) + 0.8 * tone(98.0) + 0.5 * tone(131.0)) * 0.07 * breath | |
| # 3. slow dissonant swell: two close tones beating (minor-second tension) | |
| swell_env = np.sin(np.pi * np.clip(t / dur, 0, 1)) ** 1.5 # 0 at both ends | |
| swell = (np.sin(2 * np.pi * 110.0 * t) | |
| + np.sin(2 * np.pi * 116.5 * t)) * 0.05 * swell_env | |
| # 4. distant toll ~every 5.3 s: struck tone, long decay, far and quiet | |
| toll = np.zeros_like(t) | |
| for c in (1.0, 6.3, 11.6): | |
| env = np.where(t >= c, np.exp(-1.8 * (t - c)), 0.0) | |
| strike = (np.sin(2 * np.pi * 98.0 * t) | |
| + 0.5 * np.sin(2 * np.pi * 147.0 * t) | |
| + 0.25 * np.sin(2 * np.pi * 196.0 * t)) | |
| toll += strike * env | |
| echo = np.zeros_like(t) # room tail | |
| for delay, gain in ((0.28, 0.30), (0.56, 0.15)): | |
| ds = int(delay * sr) | |
| echo[ds:] += toll[:-ds] * gain | |
| toll = (toll + echo) * 0.14 | |
| mix = sub + body + swell + toll | |
| mix = np.tanh(1.5 * mix) | |
| mix = mix / np.max(np.abs(mix)) * 0.5 # audible; no end-fade -> seamless | |
| return _wav(mix, sr) | |
| def type_tick_wav_bytes() -> bytes: | |
| """A short dry tick for the intro typewriter: a low ~180 Hz burst with a | |
| fast decay and a touch of noise, faintly detuned. Synthesized like the | |
| other stings (HF rejects committed audio binaries).""" | |
| sr = 22050 | |
| dur = 0.035 | |
| t = np.linspace(0, dur, int(sr * dur), endpoint=False) | |
| rng = np.random.default_rng(7) | |
| body = (np.sin(2 * np.pi * 180 * t) | |
| + 0.4 * np.sin(2 * np.pi * 181.2 * t) # faint detune | |
| + 0.2 * np.sin(2 * np.pi * 360 * t)) | |
| click = rng.standard_normal(len(t)) * 0.25 # a little grit at the onset | |
| env = np.exp(-t * 90) # fast percussive decay | |
| mix = (body + click) * env | |
| mix = mix / np.max(np.abs(mix)) * 0.5 | |
| return _wav(mix, sr) | |
| if __name__ == "__main__": | |
| from pathlib import Path | |
| here = Path(__file__).parent | |
| (here / "sting_preview.wav").write_bytes(sting_wav_bytes()) | |
| for i in range(3): | |
| (here / f"chime_preview_{i}.wav").write_bytes(chime_wav_bytes(i)) | |
| (here / "heartbeat_preview.wav").write_bytes(heartbeat_wav_bytes()) | |
| (here / "sigh_preview.wav").write_bytes(sigh_wav_bytes()) | |
| (here / "flatline_preview.wav").write_bytes(flatline_wav_bytes()) | |
| (here / "menu_loop_preview.wav").write_bytes(menu_loop_wav_bytes()) | |
| (here / "type_tick_preview.wav").write_bytes(type_tick_wav_bytes()) | |
| print("written sting_preview.wav + chime_preview_0/1/2.wav + menu_loop_preview.wav + type_tick_preview.wav") | |