#!/usr/bin/env python3 # Copyright (c) 2026, CIIL Anuvani-LID. Apache-2.0. """Deterministic long recordings built from the gate clips, for checking the long-audio pipeline anywhere the serving kit's gate audio is (the 10-minute and 1-hour k6 files are not distributed). Every file is a sequence of real clips (suite1k, known languages only) separated by digital silence, laid out to exercise each rule of the pipeline: single_* one language; gaps of 0.1-3 s (around the VAD's 0.3 s minimum silence and the 0.6 s merge gap) mixed_* three languages in blocks continuous_* clips back to back for over a minute: the VAD's 30 s maximum-speech split and piece tiling short_speech a 0.2 s clip and silence: speech shorter than the 0.3 s minimum span (no decision) silence 10 s of zeros (no speech) resampled_* a mixed file at 8 kHz and at 44.1 kHz (resampling; reported, not gated: the server resamples with soxr HQ, phones with sherpa-onnx's windowed-sinc resampler) Each WAV (16-bit PCM) gets a JSON with its ground-truth timeline. python3 gate/make_long_audio.py [--out results/tmp/long] """ import argparse import json import os import sys import numpy as np sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "tools")) import common # noqa: E402 SR = common.SR def suite_by_language(): g = common.gate_dir() labels = set(common.labels()) by = {} for r in (json.loads(l) for l in open(f"{g}/suite1k/manifest.jsonl")): if r["label"] in labels and r["seconds"] >= 1.0: by.setdefault(r["label"], []).append(f"{g}/suite1k/{r['file']}") return {k: sorted(v) for k, v in sorted(by.items())} def build(rng, items, gaps): """items: [(path, label)], gaps: seconds after each item -> (audio, timeline)""" parts, timeline, t = [np.zeros(int(0.5 * SR), np.float32)], [], int(0.5 * SR) for (path, label), gap in zip(items, gaps): w = common.load_wav(path) parts.append(w); timeline.append([t / SR, (t + len(w)) / SR, label]); t += len(w) z = np.zeros(int(gap * SR), np.float32) parts.append(z); t += len(z) return np.concatenate(parts), timeline def write(out, name, audio, timeline, sr=SR): import soundfile as sf if sr != SR: from fractions import Fraction from scipy.signal import resample_poly f = Fraction(sr, SR) audio = resample_poly(audio, f.numerator, f.denominator).astype(np.float32) sf.write(f"{out}/{name}.wav", np.clip(audio, -1, 1), sr, subtype="PCM_16") share = {} for s, e, l in timeline: share[l] = share.get(l, 0.0) + e - s meta = {"file": f"{name}.wav", "sample_rate": sr, "seconds": round(len(audio) / sr, 3), "dominant": max(share, key=share.get) if share else None, "timeline": timeline} json.dump(meta, open(f"{out}/{name}.json", "w"), indent=1) print(f" {name}.wav {meta['seconds']:7.1f} s {len(timeline):3d} clips dominant={meta['dominant']}") def main(): ap = argparse.ArgumentParser() ap.add_argument("--out", default=os.path.join(common.REPO, "results", "tmp", "long")) a = ap.parse_args() os.makedirs(a.out, exist_ok=True) rng = np.random.default_rng(20261008) by = suite_by_language() langs = list(by) gap_choices = [0.1, 0.25, 0.4, 0.55, 0.65, 0.8, 1.5, 3.0] def pick(lang, k): files = by[lang] idx = rng.choice(len(files), size=min(k, len(files)), replace=False) return [(files[i], lang) for i in sorted(idx)] for i, lang in enumerate(rng.choice(langs, size=6, replace=False)): items = pick(lang, 24) audio, tl = build(rng, items, rng.choice(gap_choices, size=len(items))) write(a.out, f"single_{i}_{lang}", audio, tl) mixed = [] for i in range(4): items = [] for lang in rng.choice(langs, size=3, replace=False): items += pick(lang, 10) audio, tl = build(rng, items, rng.choice(gap_choices, size=len(items))) write(a.out, f"mixed_{i}", audio, tl) mixed.append((audio, tl)) for i in range(2): items = [] for lang in rng.choice(langs, size=2, replace=False): items += pick(lang, 14) audio, tl = build(rng, items, [0.0] * len(items)) write(a.out, f"continuous_{i}", audio, tl) g = common.gate_dir() short = [r for r in (json.loads(l) for l in open(f"{g}/suite1k/manifest.jsonl")) if r["seconds"] <= 0.25][0] w = common.load_wav(f"{g}/suite1k/{short['file']}") audio = np.concatenate([np.zeros(2 * SR, np.float32), w, np.zeros(3 * SR, np.float32)]) write(a.out, "short_speech", audio, [[2.0, 2.0 + len(w) / SR, short["label"]]]) write(a.out, "silence", np.zeros(10 * SR, np.float32), []) for sr in (8000, 44100): write(a.out, f"resampled_{sr}", mixed[0][0], mixed[0][1], sr=sr) if __name__ == "__main__": main()