Spaces:
Running
Running
| import os | |
| import random | |
| from elevenlabs.client import ElevenLabs | |
| class VoiceManager: | |
| def __init__(self): | |
| self.api_key = os.getenv("ELEVENLABS_API_KEY") | |
| self.client = None | |
| if self.api_key: | |
| self.client = ElevenLabs(api_key=self.api_key) | |
| # Archetype-based Voice Mapping | |
| # We map the suspect's 'archetype' (from image metadata or role logic) or gender to these. | |
| self.voices = { | |
| "male": { | |
| "executive": "ErXwobaYiN019PkySvjV", # Antoni (Polished) | |
| "worker": "wI49R6YUU5NNP1h0CECc", # Josh (Casual) | |
| "criminal": "VR6AewLTigWg4xSOukaG", # Arnold (Gravelly) | |
| "artist": "D38z5RcWu1voky8WS1ja", # Fin (Expressive) | |
| "default": "TxGEqnHWrfWFTfGW9XjX" # Josh | |
| }, | |
| "female": { | |
| "executive": "21m00Tcm4TlvDq8ikWAM", # Rachel (Professional) | |
| "worker": "AZnzlk1XvdvUeBnXmlld", # Domi (Strong) | |
| "socialite": "EXAVITQu4vr4xnSDxMaL", # Bella (Intense) | |
| "artist": "nDJIICjR9zfJExIFeSCN", # Bella | |
| "default": "6u6JbqKdaQy89ENzLSju" # Rachel | |
| } | |
| } | |
| def assign_voice(self, gender, role=""): | |
| """Pick a voice based on gender and role archetype.""" | |
| g = "male" if gender.lower() == "male" else "female" | |
| role = role.lower() | |
| # Simple archetype matching logic (matches game_logic.js) | |
| archetype = "default" | |
| if "ceo" in role or "cfo" in role or "manager" in role or "dealer" in role: | |
| archetype = "executive" | |
| elif "janitor" in role or "chef" in role or "caterer" in role: | |
| archetype = "worker" | |
| elif "artist" in role or "curator" in role: | |
| archetype = "artist" | |
| elif "heir" in role or "collector" in role or "sister" in role: | |
| archetype = "socialite" | |
| elif "ex-" in role or "customer" in role: | |
| archetype = "criminal" | |
| # Return specific voice or random if not found? | |
| # Better to return specific to ensure personality match. | |
| voice_map = self.voices[g] | |
| return voice_map.get(archetype, voice_map["default"]) | |
| def generate_audio(self, text, voice_id): | |
| """Generates audio bytes from text.""" | |
| if not self.client: | |
| print("Warning: No ElevenLabs API Key. Skipping TTS.") | |
| return None | |
| try: | |
| # Modern SDK usage | |
| audio_generator = self.client.text_to_speech.convert( | |
| text=text, | |
| voice_id=voice_id, | |
| model_id="eleven_monolingual_v1" | |
| ) | |
| # Consolidate generator into bytes | |
| audio_bytes = b"".join(audio_generator) | |
| return audio_bytes | |
| except Exception as e: | |
| print(f"ElevenLabs Error: {e}") | |
| return None |