File size: 3,286 Bytes
e3f7487
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
"""Build the 15.08 s held-out references from the dataset dry sources.

The a2a render length is 377 frames @ 25 fps = 15.08 s, but every dataset
source is exactly 6.000 s. So the reference is the dry clip repeated with a
short gap and trimmed to length — the same construction used for the eval
references on 08-13, which lived inline in vast_push.sh and was therefore lost
when that box died. It is reproducible, so it belongs in a file.

Held-out on purpose: beat_02 and male_voice_02 are the VALIDATION split, never
seen in training, and both are owned/cleared — which makes them the only
sources usable for public examples.

Reads and writes 24-bit stereo WAV at 48 kHz with no external dependencies
beyond numpy, so it runs on a bare pod before any venv exists.
"""

import argparse
import sys
import wave
from pathlib import Path

import numpy as np

SR = 48000
TARGET_S = 15.08
GAP_S = 1.54


def read24(path: Path) -> np.ndarray:
    with wave.open(str(path), "rb") as f:
        if f.getsampwidth() != 3:
            raise SystemExit(f"{path}: expected 24-bit, got {f.getsampwidth()*8}-bit")
        if f.getframerate() != SR:
            raise SystemExit(f"{path}: expected {SR} Hz, got {f.getframerate()}")
        ch = f.getnchannels()
        raw = f.readframes(f.getnframes())
    b = np.frombuffer(raw, dtype=np.uint8).reshape(-1, ch, 3).astype(np.int32)
    v = b[..., 0] | (b[..., 1] << 8) | (b[..., 2] << 16)
    v = np.where(v >= 1 << 23, v - (1 << 24), v)          # sign-extend
    return v.astype(np.float64) / 8388607.0


def save24(x: np.ndarray, path: Path) -> None:
    i = (np.clip(x, -1.0, 1.0) * 8388607.0).astype(np.int32)
    bb = np.stack([i & 255, (i >> 8) & 255, (i >> 16) & 255], axis=-1).astype(np.uint8)
    with wave.open(str(path), "wb") as w:
        w.setnchannels(x.shape[1])
        w.setsampwidth(3)
        w.setframerate(SR)
        w.writeframes(bb.tobytes())


def build(src: Path, dst: Path) -> None:
    x = read24(src)
    n_target = int(TARGET_S * SR)
    gap = np.zeros((int(GAP_S * SR), x.shape[1]))
    out = x
    while len(out) < n_target:                            # repeat until long enough
        out = np.concatenate([out, gap, x])
    out = out[:n_target]
    save24(out, dst)
    peak = float(np.max(np.abs(out))) or 1e-12
    print(f"  {dst.name}: {len(out)/SR:.2f}s, peak {20*np.log10(peak):.1f} dBFS")


def main() -> int:
    p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    p.add_argument("--dry-dir", default="/workspace/AUDIO-LTX-LORA/Dataset/v5-rooms/medium_room_1_9/dry")
    p.add_argument("--out-dir", default="/workspace")
    a = p.parse_args()

    pairs = [("male_voice_02.wav", "v5_eval_voice_15s.wav"),
             ("beat_02.wav", "v5_eval_beat_15s.wav")]
    dry, out = Path(a.dry_dir), Path(a.out_dir)
    missing = [s for s, _ in pairs if not (dry / s).exists()]
    if missing:
        print(f"MISSING held-out sources in {dry}: {missing}", file=sys.stderr)
        print("The payload subset must include the VALIDATION split, not just train rows.", file=sys.stderr)
        return 2
    for src, dest in pairs:
        build(dry / src, out / dest)
    print("REFS_BUILT")
    return 0


if __name__ == "__main__":
    sys.exit(main())