"""List and audition the PC voices available to the simulator. python scripts/voices.py # list what is installed python scripts/voices.py --demo # speak a sample line in each voice python scripts/voices.py --try Zira # hear one voice, with .env's rate/volume Whichever you prefer goes in .env: MOCK_LOCAL_AUDIO=true MOCK_VOICE=Zira IMPORTANT - this is the *simulator's* voice, not the robot's. The real AGIBOT A3 synthesises speech on-board; AgiBot does not publish which engine or timbre it uses, so no PC voice can be claimed to match it. This is a stand-in for rehearsing a demo. See docs/AGIBOT_A3_INTEGRATION.md. """ from __future__ import annotations import argparse import platform import sys import time from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1])) def _use_utf8_console() -> None: for stream in (sys.stdout, sys.stderr): try: if stream is not None and hasattr(stream, "reconfigure"): stream.reconfigure(encoding="utf-8", errors="replace") except Exception: pass _use_utf8_console() from backend.config.settings import get_settings # noqa: E402 from backend.robot.local_audio import create_local_voice, list_local_voices # noqa: E402 SAMPLE = "Good afternoon, and welcome to our showroom. I am the AGIBOT A3 humanoid robot." def speak_with(hint, text, rate, volume, pitch=0) -> None: voice = create_local_voice(voice_hint=hint, rate=rate, volume=volume, pitch=pitch) if voice is None: print(" No speech engine available on this PC.") return try: voice.start(text) while voice.is_speaking(): time.sleep(0.05) finally: voice.close() def main() -> None: parser = argparse.ArgumentParser(description="List and audition simulator voices.") parser.add_argument("--demo", action="store_true", help="Speak a sample in every voice.") parser.add_argument("--try", dest="which", metavar="NAME", help="Speak a sample in one voice.") parser.add_argument("--text", default=SAMPLE, help="Say this instead of the default sample.") args = parser.parse_args() settings = get_settings() rate, volume = settings.mock.speech_rate, settings.mock.speech_volume pitch = settings.mock.speech_pitch if args.which: print('\n Voice "{0}" (rate {1}, volume {2}, pitch {3})'.format( args.which, rate, volume, pitch)) print(" " + args.text + "\n") speak_with(args.which, args.text, rate, volume, pitch) return if settings.mock.voice_engine == "gemini": from backend.robot.gemini_voice import VOICES as GEMINI_VOICES current = (settings.mock.gemini_voice or "").lower() print("\n Gemini neural voices (active: MOCK_VOICE_ENGINE=gemini)") print(" " + "-" * 58) for name, tone in sorted(GEMINI_VOICES.items()): mark = "*" if name.lower() == current else " " note = " <- young + male, closest to the robot's Yunxiao" \ if name in ("Puck", "Fenrir") else "" print(" {0} {1:<16} {2}{3}".format(mark, name, tone, note)) print("") print(" * = current GEMINI_VOICE. Change it in .env, then warm your lines:") print(' python scripts/warm_voice.py "Welcome to our showroom."') voices = list_local_voices() print("\n System voices on this PC (used when MOCK_VOICE_ENGINE=system,") print(" and as the fallback if Gemini fails)") print(" " + "-" * 58) if not voices: if platform.system().lower().startswith("win"): print(" None found.") else: print(" Listing is Windows-only; on macOS the simulator uses `say`,") print(" on Linux `espeak-ng`.") return for description, source in voices: marker = "*" if settings.mock.voice and settings.mock.voice.lower() in description.lower() else " " print(" {0} {1:<48} [{2}]".format(marker, description, source)) print("") print(" * = currently selected by MOCK_VOICE={0}".format(settings.mock.voice or "(unset)")) print(" 'onecore' voices are Windows' newer, better-sounding set.") print("") print(" Hear one: python scripts/voices.py --try Zira") print(" Hear all: python scripts/voices.py --demo") print("") print(" Want more voices (including Mandarin, to match the A3's demo language)?") print(" Windows Settings > Time & language > Speech > Manage voices > Add voices.") print("") if args.demo: for description, _ in voices: short = description.replace("Microsoft ", "").split(" - ")[0] print(" ->", description) speak_with(description, "{0} speaking. {1}".format(short, args.text), rate, volume, pitch) time.sleep(0.4) print("") if __name__ == "__main__": main()