ManuBo's picture
v0.2.0: quality score and integration
b89a72f
Raw
History Blame Contribute Delete
4.96 kB
from __future__ import annotations
import argparse
import sys
from pathlib import Path
from audio_qa.pipeline import check_file, check_directory, save_report
def print_file_result(result: dict):
fname = Path(result.get("file", "unknown")).name
if result.get("error"):
print(" ERROR %s: %s" % (fname, result["error"]))
return
status = "PASS" if result["all_passed"] else "FAIL"
score = result.get("quality_score", 0)
grade = result.get("grade", "?")
print(" %s %s -- %d/%d passed | Score: %.1f/10 (%s) | %.1fs, %dHz" % (
status, fname, result["num_passed"], result["num_checks"],
score, grade,
result.get("duration_s", 0), result.get("sample_rate", 0),
))
for check in result.get("checks", []):
if not check.get("passed"):
icon = "WARN" if check.get("severity") == "medium" else "FAIL"
detail = ""
name = check["check"]
if name == "background_noise":
detail = "SNR=%.1fdB" % check.get("snr_db", 0)
elif name == "clipping":
detail = "ratio=%s" % check.get("clip_ratio")
elif name == "silence":
detail = ", ".join(check.get("issues", []))
elif name == "loudness":
detail = "LUFS=%.1fdB (dev=%.1f)" % (check.get("lufs", 0), check.get("deviation_db", 0))
elif name == "duration":
detail = ", ".join(check.get("issues", []))
elif name == "sample_rate":
detail = ", ".join(check.get("issues", []))
elif name == "tts_metallic":
detail = "flatness_ratio=%s" % check.get("metallic_frame_ratio")
elif name == "tts_repetition":
detail = "corr=%s" % check.get("max_correlation")
elif name == "channel":
detail = ", ".join(check.get("issues", []))
elif name == "upsampling":
detail = "claimed=%sHz, estimated_original=%sHz" % (
check.get("claimed_sr"), check.get("estimated_original_sr"))
elif name == "transcript_ratio":
detail = "%.1f CPS" % check.get("cps", 0)
print(" %s %s: %s" % (icon, name, detail))
def main():
parser = argparse.ArgumentParser(
description="audio-qa -- lint audio datasets for TTS, ASR, and voice-cloning"
)
parser.add_argument("path", help="Audio file or directory")
parser.add_argument("--report", help="Save JSON report to this path")
parser.add_argument("--csv", help="Save CSV summary to this path")
parser.add_argument("--manifest", help="Save clean file list to this path")
parser.add_argument("--expected-sr", type=int, default=None)
parser.add_argument("--min-duration", type=float, default=0.5)
parser.add_argument("--max-duration", type=float, default=30.0)
parser.add_argument("--snr-threshold", type=float, default=15.0)
parser.add_argument("--target-lufs", type=float, default=-23.0)
parser.add_argument("--workers", type=int, default=4)
args = parser.parse_args()
path = Path(args.path)
kwargs = dict(
expected_sr=args.expected_sr,
min_duration=args.min_duration,
max_duration=args.max_duration,
snr_threshold=args.snr_threshold,
target_lufs=args.target_lufs,
)
if path.is_file():
print("\nChecking: %s\n" % path.name)
result = check_file(str(path), **kwargs)
print_file_result(result)
if args.report:
save_report(result, args.report)
print("\nReport saved to %s" % args.report)
elif path.is_dir():
print("\nChecking directory: %s\n" % path)
report = check_directory(str(path), workers=args.workers, **kwargs)
print(report.summary())
# Show flagged files
flagged = [r for r in report.file_results
if not r.get("all_passed") and not r.get("error")]
if flagged:
print("\nFlagged files (%d):" % len(flagged))
for r in flagged[:20]:
print_file_result(r)
dupes = report.duplicates
if dupes:
print("\nDuplicate pairs (%d):" % len(dupes))
for d in dupes[:10]:
print(" DUP %s <-> %s (sim=%s)" % (
Path(d["file_a"]).name, Path(d["file_b"]).name, d["similarity"]))
if args.report:
report.to_json(args.report)
print("\nJSON report saved to %s" % args.report)
if args.csv:
report.to_csv(args.csv)
print("CSV report saved to %s" % args.csv)
if args.manifest:
report.export_clean_manifest(args.manifest)
print("Clean manifest saved to %s (%d files)" % (args.manifest, len(report.clean_file_list())))
else:
print("Error: %s not found" % path)
sys.exit(1)
if __name__ == "__main__":
main()