Download ComfyUI-MiniMaxH3-Context-Loop/ComfyUI-MAINodes/tests/test_audio_smear.py from dzmdd/Fast_deploy: direct link, hf CLI and curl.
- Browser
- Download file 3.18 kB
-
https://huggingface.co/dzmdd/Fast_deploy/resolve/main/ComfyUI-MiniMaxH3-Context-Loop/ComfyUI-MAINodes/tests/test_audio_smear.py
- Command line
-
hf download hf://dzmdd/Fast_deploy/ComfyUI-MiniMaxH3-Context-Loop/ComfyUI-MAINodes/tests/test_audio_smear.py
-
curl -L -o test_audio_smear.py https://huggingface.co/dzmdd/Fast_deploy/resolve/main/ComfyUI-MiniMaxH3-Context-Loop/ComfyUI-MAINodes/tests/test_audio_smear.py
3.18 kB
| #!/usr/bin/env python3 | |
| """Regression test for H3 Audio Smear, the inverse of H3 Audio Recover. | |
| python tests/test_audio_smear.py | |
| Synthetic, no GPU, no files. Smear stretches a world-clock track onto the | |
| dilated clock the smeared video init lives on; Recover compresses it back. | |
| The pair has to close: | |
| 1. smeared length is sample-exact against sum(holds) frames | |
| 2. smear -> recover returns the source length sample-exact | |
| 3. the round trip preserves TIMING (envelope correlation), which is the | |
| only property the seed has to carry - a phase vocoder does not | |
| preserve phase, so sample-wise correlation is expected to be poor and | |
| is not asserted | |
| 4. an all-ones hold map is a no-op | |
| Why the timing matters: the seeded init exists so pass 2 renders a slowed | |
| performance instead of inventing one at natural rate. If the stretch | |
| misplaces events, the lips follow the wrong clock and Exact Recover / | |
| Audio Recover compress something that was never slow. | |
| Exit code 0 = pass. | |
| """ | |
| import json | |
| import math | |
| import os | |
| import sys | |
| import torch | |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| from motion import H3AudioRecover, H3AudioSmear # noqa: E402 | |
| SR, FPS = 32000, 24 | |
| FAIL = [] | |
| def check(ok, msg): | |
| print(("PASS " if ok else "FAIL ") + msg) | |
| if not ok: | |
| FAIL.append(msg) | |
| def tone(n_frames): | |
| """Amplitude-modulated tone: timing is legible in the envelope.""" | |
| t = torch.arange(int(n_frames / FPS * SR)) / SR | |
| x = torch.sin(2 * math.pi * 220 * t) * (0.5 + 0.5 * torch.sin(2 * math.pi * 5 * t)) | |
| return {"waveform": x.float()[None, None, :], "sample_rate": SR} | |
| def envelope(v, w=256): | |
| return torch.nn.functional.avg_pool1d(v.abs()[None, None], w, w)[0, 0] | |
| def corr(a, b): | |
| n = min(a.shape[0], b.shape[0]) | |
| a, b = a[:n], b[:n] | |
| return float(torch.dot(a - a.mean(), b - b.mean()) | |
| / (a.std() * b.std() * n + 1e-9)) | |
| def main(): | |
| # a burst in the middle, calm either side - the shape the oracle emits | |
| holds = [1] * 30 + [4] * 30 + [1] * 30 | |
| hm = json.dumps({"holds": holds}) | |
| src = tone(len(holds)) | |
| n_src = src["waveform"].shape[-1] | |
| sm = H3AudioSmear().smear(src, hm, FPS)[0] | |
| want = int(round(sum(holds) * SR / FPS)) | |
| check(abs(sm["waveform"].shape[-1] - want) <= 1, | |
| f"smeared length sample-exact: {sm['waveform'].shape[-1]} vs {want}") | |
| rt = H3AudioRecover().recover(sm, hm, FPS)[0] | |
| check(abs(rt["waveform"].shape[-1] - n_src) <= 1, | |
| f"round trip returns the world clock: {rt['waveform'].shape[-1]} vs {n_src}") | |
| c = corr(envelope(src["waveform"].reshape(-1)), envelope(rt["waveform"].reshape(-1))) | |
| check(c > 0.9, f"round trip preserves timing: envelope corr {c:+.3f} (> 0.9)") | |
| # an unheld map must not touch the track's length | |
| flat = json.dumps({"holds": [1] * 60}) | |
| f = H3AudioSmear().smear(tone(60), flat, FPS)[0] | |
| check(abs(f["waveform"].shape[-1] - int(round(60 * SR / FPS))) <= 1, | |
| "all-ones hold map is a no-op on length") | |
| print("\n" + ("ALL PASS" if not FAIL else f"{len(FAIL)} FAILED")) | |
| return 1 if FAIL else 0 | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |