#!/usr/bin/env python3
"""Generate assets/office_ambience.wav — low, unintelligible office murmur.

Mixed continuously into the outbound call audio (SoundfileMixer) so the caller hears a
live-office room tone instead of dead digital silence while the agent thinks. Built from
Kokoro voices speaking mundane phrases, overlapped and heavily low-pass filtered so no
words are intelligible (nothing that could be mistaken for real conversation/PHI), plus
a touch of room noise. Output: 60s mono 16 kHz PCM16 loop, quiet by design (the mixer
volume scales it further).

Run inside the pipecat venv: python scripts/make_ambience.py
"""
import os

import numpy as np
import soundfile as sf
from kokoro_onnx import Kokoro

HERE = os.path.dirname(os.path.abspath(__file__))
PROJ = os.path.dirname(HERE)
MODEL_DIR = os.environ.get("KOKORO_MODEL_DIR", "/home/tocmo0nlord/pipecat-run/models")
OUT = os.path.join(PROJ, "assets", "office_ambience.wav")

SR = 16000          # match PIPELINE_SAMPLE_RATE (transport output rate)
DUR = 60.0          # loop length in seconds
RNG = np.random.default_rng(42)

PHRASES = [
    "Okay, so I'll move that over to Thursday and send the reminder out this afternoon.",
    "Could you pull the file for the three o'clock? I think it's already up front.",
    "Yes, we got the shipment in this morning, it's in the back on the second shelf.",
    "Let me transfer you over, one moment please, thank you so much for holding.",
    "The afternoon looks pretty full but the morning still has a couple of openings.",
    "I'll leave a note for the doctor and we'll follow up first thing tomorrow.",
    "They said the delivery should arrive before noon so we should be all set.",
    "Can you double check the calendar for next Tuesday? I think there's a conflict.",
]
VOICES = ["af_bella", "am_michael", "bf_emma", "am_adam", "af_nicole", "bm_george"]


def lowpass(x, cutoff_hz, sr):
    """Simple FFT brick-wall low-pass — fine for ambience shaping."""
    X = np.fft.rfft(x)
    freqs = np.fft.rfftfreq(len(x), 1 / sr)
    X[freqs > cutoff_hz] = 0
    return np.fft.irfft(X, n=len(x))


def main():
    k = Kokoro(os.path.join(MODEL_DIR, "kokoro-v1.0.onnx"),
               os.path.join(MODEL_DIR, "voices-v1.0.bin"))
    bed = np.zeros(int(SR * DUR), dtype=np.float64)

    # ~18 murmured utterances scattered through the minute, different voices/speeds.
    for i in range(18):
        text = PHRASES[i % len(PHRASES)]
        voice = VOICES[i % len(VOICES)]
        samples, sr0 = k.create(text, voice=voice, speed=float(RNG.uniform(0.9, 1.1)))
        # Resample to SR by linear interpolation (quality is irrelevant post-filter).
        t_src = np.arange(len(samples)) / sr0
        t_dst = np.arange(int(len(samples) * SR / sr0)) / SR
        s = np.interp(t_dst, t_src, samples.astype(np.float64))
        s = lowpass(s, 900, SR)                # muffle: through-the-wall murmur
        s *= RNG.uniform(0.25, 0.5)            # each talker is quiet and uneven
        start = int(RNG.uniform(0, DUR - len(s) / SR) * SR)
        bed[start:start + len(s)] += s

    # Gentle room tone under the voices (shaped noise), so pauses aren't pure silence.
    noise = lowpass(RNG.standard_normal(len(bed)), 400, SR) * 0.02
    bed += noise

    # Loop seamlessly: crossfade the last second into the first.
    xf = SR
    ramp = np.linspace(0, 1, xf)
    bed[:xf] = bed[:xf] * ramp + bed[-xf:] * (1 - ramp)
    bed = bed[:-xf]

    # Normalize conservatively — this is a QUIET bed; mixer volume scales it down further.
    bed = bed / (np.abs(bed).max() + 1e-9) * 0.35
    os.makedirs(os.path.dirname(OUT), exist_ok=True)
    sf.write(OUT, bed.astype(np.float32), SR, subtype="PCM_16")
    print(f"wrote {OUT}: {len(bed)/SR:.1f}s @ {SR}Hz, peak {np.abs(bed).max():.2f}")


if __name__ == "__main__":
    main()
