HOA7 Spatial Field Decoder (hoa64 v0.5.0): 7th-order Ambisonics encode/decode, Wigner-D rotation, DOA analysis, vision fuse, diffusion conditioning
570b87b verified | """Detector, live pipeline (file), conditioning tests.""" | |
| from __future__ import annotations | |
| import json | |
| import sys | |
| import tempfile | |
| from pathlib import Path | |
| import numpy as np | |
| sys.path.insert(0, str(Path(__file__).resolve().parents[1])) | |
| from hoa64.analysis import angular_error_deg | |
| from hoa64.audio_io import write_wav | |
| from hoa64.conditioning import build_conditioning, comfy_txt2img_payload, spatial_prompt_fragment | |
| from hoa64.detector import ( | |
| Detection, | |
| detections_to_sphere_boxes, | |
| load_yolo_labels, | |
| write_demo_image_with_box, | |
| ) | |
| from hoa64.live_audio import audio_to_hoa_stream, live_report_from_file, stereo_to_order1_hoa | |
| from hoa64.synth import tone | |
| from hoa64.vision import fuse_reports, report_from_boxes | |
| def test_demo_image_detect_sphere(): | |
| with tempfile.TemporaryDirectory() as td: | |
| img = Path(td) / "demo.png" | |
| det = write_demo_image_with_box(img) | |
| boxes = detections_to_sphere_boxes([det]) | |
| assert boxes[0]["label"] == "demo_object" | |
| # center of box ~ (0.45, 0.5) → slight left az positive? cx=0.45 → az=(0.5-0.45)*90=+4.5 | |
| assert abs(boxes[0]["az"] - 4.5) < 1.0 | |
| rep = report_from_boxes(boxes, max_order=3) | |
| assert rep.energy > 0 | |
| def test_yolo_labels(): | |
| with tempfile.TemporaryDirectory() as td: | |
| p = Path(td) / "labels.txt" | |
| # class 0 center front-ish | |
| p.write_text("0 0.5 0.5 0.2 0.2 0.9\n") | |
| dets = load_yolo_labels(p) | |
| assert len(dets) == 1 | |
| boxes = detections_to_sphere_boxes(dets) | |
| assert abs(boxes[0]["az"]) < 1.0 # center → az≈0 | |
| def test_live_pipeline_from_synth_wav(): | |
| with tempfile.TemporaryDirectory() as td: | |
| wav = Path(td) / "m.wav" | |
| sr = 16000 | |
| sig = tone(800, 0.3, sr, amplitude=0.4) | |
| write_wav(wav, sig, sr) | |
| rep = live_report_from_file(wav, az_deg=30.0, el_deg=0.0, max_order=1) | |
| err = angular_error_deg(30.0, 0.0, rep.doa_az_deg, rep.doa_el_deg) | |
| assert err < 8.0, f"err={err}" | |
| def test_stereo_pseudo_hoa(): | |
| sr = 8000 | |
| L = tone(400, 0.2, sr, amplitude=0.5) | |
| R = tone(400, 0.2, sr, amplitude=0.1) | |
| hoa = stereo_to_order1_hoa(L, R, width_az_deg=40) | |
| assert hoa.shape[0] == 64 | |
| # left-dominant → positive Y energy | |
| assert np.mean(hoa[1] ** 2) > 0 | |
| def test_conditioning_from_fuse(): | |
| audio = {"doa_az_deg": 15.0, "doa_el_deg": 0.0, "energy": 1.0, "kind": "spatial_field"} | |
| vision = { | |
| "doa_az_deg": 18.0, | |
| "doa_el_deg": 2.0, | |
| "energy": 0.5, | |
| "kind": "spatial_vision", | |
| "peak_az_deg": 18.0, | |
| "peak_el_deg": 2.0, | |
| } | |
| fused = fuse_reports(audio, vision) | |
| cond = build_conditioning(fused, base_prompt="cinematic room", style="natural") | |
| assert "cinematic room" in cond["positive_prompt"] | |
| assert cond["schema"] == "spatial-hoa.conditioning.v1" | |
| assert "control_vector" in cond | |
| frag = spatial_prompt_fragment(fused, style="tags") | |
| assert "spatial-az" in frag | |
| # offline payload with explicit checkpoint (no Comfy required) | |
| wf = comfy_txt2img_payload( | |
| cond, checkpoint="sd_xl_base_1.0.safetensors", auto_checkpoint=False | |
| ) | |
| assert "3" in wf and wf["6"]["inputs"]["text"] | |
| assert wf["4"]["inputs"]["ckpt_name"] == "sd_xl_base_1.0.safetensors" | |
| assert wf["5"]["inputs"]["width"] == 1024 # XL default size | |
| if __name__ == "__main__": | |
| test_demo_image_detect_sphere() | |
| print("OK test_demo_image_detect_sphere") | |
| test_yolo_labels() | |
| print("OK test_yolo_labels") | |
| test_live_pipeline_from_synth_wav() | |
| print("OK test_live_pipeline_from_synth_wav") | |
| test_stereo_pseudo_hoa() | |
| print("OK test_stereo_pseudo_hoa") | |
| test_conditioning_from_fuse() | |
| print("OK test_conditioning_from_fuse") | |