From 469d372338dc718fdf1218e19a34676f935c3da8 Mon Sep 17 00:00:00 2001 From: Adolfo Reyna Date: Fri, 7 Aug 2026 20:20:36 -0400 Subject: [PATCH] Make voice_tool dependency-free and add voice alias mappings for macOS vs Kokoro names --- voice_manager.py | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/voice_manager.py b/voice_manager.py index 8d05b99..6483955 100644 --- a/voice_manager.py +++ b/voice_manager.py @@ -4,8 +4,13 @@ Supports both Kokoro TTS voices and macOS System voices, persisting user voice p """ import json +import logging from pathlib import Path -from loguru import logger + +try: + from loguru import logger +except ImportError: + logger = logging.getLogger("voice_manager") # Supported Kokoro TTS voices categorized by style KOKORO_VOICES = { @@ -32,6 +37,19 @@ MACOS_VOICES = { "Alex": "US Male", } +VOICE_ALIASES = { + "daniel": "bm_george", # British Male + "moira": "bf_emma", # British Female + "samantha": "af_bella", # US Female + "alex": "am_michael", # US Male + "karen": "bf_isabella", # AU/UK Female + "michael": "am_michael", + "bella": "af_bella", + "heart": "af_heart", + "fenrir": "am_fenrir", + "adam": "am_adam", +} + class VoiceManager: """Manages active voice settings and dynamic voice switching.""" @@ -78,21 +96,27 @@ class VoiceManager: voice_name = voice_name.strip() matched_voice = None - # Exact match or fuzzy match - for v in {**KOKORO_VOICES, **MACOS_VOICES}: - if voice_name.lower() == v.lower(): - matched_voice = v - break + # Check voice aliases first (e.g. Daniel -> bm_george) + clean_name = voice_name.lower() + if clean_name in VOICE_ALIASES: + matched_voice = VOICE_ALIASES[clean_name] + # Exact match or fuzzy match if not matched_voice: - # Partial match search - for v in {**KOKORO_VOICES, **MACOS_VOICES}: - if voice_name.lower() in v.lower(): + for v in KOKORO_VOICES: + if clean_name == v.lower(): matched_voice = v break if not matched_voice: - available = ", ".join(list(KOKORO_VOICES.keys()) + list(MACOS_VOICES.keys())) + # Partial match search + for v in KOKORO_VOICES: + if clean_name in v.lower(): + matched_voice = v + break + + if not matched_voice: + available = ", ".join(list(KOKORO_VOICES.keys())) return False, f"Voice '{voice_name}' not found. Available voices: {available}" self._active_voice = matched_voice