voice_audio_multi/tests/firered_vad/check_parity.py
Fabio c36510c21e
Some checks failed
Linux Build / Linux cpu (push) Has been cancelled
Linux Build / Linux cpu-clang (push) Has been cancelled
Linux Build / Linux vulkan (push) Has been cancelled
mac-build / macOS CPU (push) Has been cancelled
Nix Build / Nix (cpu) (push) Has been cancelled
Nix Build / Nix (vulkan) (push) Has been cancelled
windows-build / Windows CPU (push) Has been cancelled
Initial import: audio.cpp with Orange Pi documentation
2026-11-04 19:05:22 +01:00

46 lines
2.4 KiB
Python

#!/usr/bin/env python3
"""Check exported native FireRed VAD outputs against the official Python fixtures."""
import argparse
import json
from pathlib import Path
import numpy as np
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--reference", required=True)
parser.add_argument("--native", required=True)
parser.add_argument("--probability-tolerance", type=float, default=0.002)
parser.add_argument("--timestamp-tolerance", type=float, default=0)
args = parser.parse_args()
metrics = {}
for suffix in (".features.f32", ".probs.f32", ".reference_features_probs.f32", ".chunked_probs.f32"):
native_path = Path(args.native + suffix)
if suffix == ".chunked_probs.f32" and not native_path.exists():
continue
reference_suffix = ".probs.f32" if suffix == ".reference_features_probs.f32" else suffix
reference = np.fromfile(args.reference + reference_suffix, dtype="<f4")
native = np.fromfile(native_path, dtype="<f4")
if native.shape != reference.shape or not np.isfinite(native).all():
raise AssertionError(f"Invalid output shape or non-finite values: {suffix}")
maximum = float(np.max(np.abs(native - reference), initial=0))
metrics[suffix] = {"max_abs": maximum, "rmse": float(np.sqrt(np.mean((native - reference) ** 2))) if native.size else 0}
# The framework and kaldi-native-fbank use different FFT/filterbank
# rounding. Bound both the worst bin and the overall normalized error.
tolerance = 0.001 if suffix == ".features.f32" else args.probability_tolerance
if maximum > tolerance:
raise AssertionError(f"{suffix}: max_abs={maximum} exceeds {tolerance}")
if suffix == ".features.f32" and metrics[suffix]["rmse"] > 1e-5:
raise AssertionError(f"Frontend RMSE exceeds 1e-5: {metrics[suffix]}")
reference = np.asarray(json.loads(Path(args.reference + ".segments.json").read_text()))
native = np.asarray(json.loads(Path(args.native + ".segments.json").read_text()))
if native.shape != reference.shape or not np.allclose(native, reference, rtol=0, atol=args.timestamp_tolerance):
raise AssertionError(f"Timestamp mismatch: reference={reference.tolist()}, native={native.tolist()}")
metrics["timestamps"] = "passed"
print(json.dumps(metrics, indent=2))
if __name__ == "__main__":
main()