"""Audition the configured Edge voice and pronunciation spellings with clean captions. @author Laurent Declercq l.declercq@agon-innovation.ch @version 20260921 """ import argparse from pathlib import Path import shutil import subprocess import sys from cli import PYTHON, TOOLS from pipeline import ( ROOT, SOURCE, caption_cues, edge_options, ensure_audio, pronunciation_text, read_json, write_json, ) def main(): """Generate an isolated MP3 audition using the video pipeline's speech settings.""" parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) parser.add_argument("--locale", choices=["fr", "en", "de"], default="fr") parser.add_argument( "--voice", help="Override the Edge voice for this audition only." ) parser.add_argument("--rate", help="Override speaking speed, for example +0%%.") parser.add_argument("--text-file", type=Path) parser.add_argument("--output", type=Path) parser.add_argument("--offline", action="store_true") parser.add_argument("--force", action="store_true") args = parser.parse_args() if args.offline and args.force: parser.error("--offline and --force cannot be combined.") if Path(sys.prefix).resolve() != TOOLS.resolve(): if not PYTHON.is_file(): parser.error("Run yarn presentation:setup first.") raise SystemExit( subprocess.run( [str(PYTHON), str(Path(__file__).resolve()), *sys.argv[1:]] ).returncode ) settings = read_json(SOURCE / "settings.json") if args.voice: settings["voices"][args.locale] = args.voice if args.rate: settings.setdefault("rates", {})[args.locale] = args.rate rate, aliases = edge_options(settings, args.locale) source = args.text_file or SOURCE / "samples" / f"{args.locale}.txt" text = source.read_text(encoding="utf-8").strip() if not text: parser.error("The audition text must not be empty.") output = ( args.output or ROOT / ".build/presentation-voice-samples-edge" / f"edge-{args.locale}.mp3" ) if output.suffix.lower() != ".mp3": parser.error("The audition output must have a .mp3 extension.") audio, words = ensure_audio( {"id": "audition", "narration": text}, args.locale, settings, ROOT / ".build/presentation/edge-auditions", args.offline, args.force, ) output.parent.mkdir(parents=True, exist_ok=True) shutil.copyfile(audio, output) output.with_suffix(".txt").write_text(text + "\n", encoding="utf-8") spoken = pronunciation_text(text, aliases) output.with_suffix(".spoken.txt").write_text(spoken + "\n", encoding="utf-8") output.with_suffix(".vtt").write_text( "WEBVTT\n\n" + "\n".join(caption_cues(words, text, 0)), encoding="utf-8" ) write_json( output.with_suffix(".json"), { "backend": "edge", "voice": settings["voices"][args.locale], "rate": rate, "pitch": "+0Hz", "locale": args.locale, "text": text, "spokenText": spoken, "pronunciations": aliases, }, ) print(f"Generated {output}.") if __name__ == "__main__": try: main() except (OSError, ValueError, RuntimeError, ImportError) as error: print(f"Edge audition failed: {error}", file=sys.stderr) raise SystemExit(1) from error