Spaces:
Sleeping
Sleeping
| """Text-to-Speech Service using ElevenLabs""" | |
| import os | |
| from typing import Optional, List, Dict | |
| from elevenlabs import ElevenLabs, VoiceSettings | |
| # Voice options mapping (name -> voice_id) | |
| VOICE_OPTIONS = { | |
| "Rachel (Female)": "21m00Tcm4TlvDq8ikWAM", # Default - Rachel | |
| "Lera (Female)": "EXAVITQu4vr4xnSDxMaL", # Lera - female voice | |
| "Bella (Female)": "EXAVITQu4vr4xnSDxMaL", # Alternative female | |
| "Antoni (Male)": "ErXwobaYiN019PkySvjV", # Antoni - male voice | |
| "Arnold (Male)": "VR6AewLTigWG4xSOukaG", # Arnold - male voice | |
| "Adam (Male)": "pNInz6obpgDQGcFmaJgB", # Adam - male voice | |
| "Domi (Female)": "AZnzlk1XvdvUeBnXmlld", # Domi - female voice | |
| "Elli (Female)": "MF3mGyEYCl7XYWbV9V6O", # Elli - female voice | |
| "Josh (Male)": "TxGEqnHWrfWFTfGW9XjX", # Josh - male voice | |
| "Sam (Male)": "yoZ06aMxZJJ28mfd3POQ", # Sam - male voice | |
| } | |
| class TTSService: | |
| """Text-to-Speech service using ElevenLabs API""" | |
| def __init__(self, api_key: str, voice_id: str = "21m00Tcm4TlvDq8ikWAM"): | |
| """ | |
| Initialize TTS service | |
| Args: | |
| api_key: ElevenLabs API key | |
| voice_id: Voice ID to use (default: Rachel) | |
| """ | |
| self.api_key = api_key | |
| self.voice_id = voice_id | |
| self.client = None | |
| self.available = False | |
| if api_key: | |
| try: | |
| self.client = ElevenLabs(api_key=api_key) | |
| self.available = True | |
| except Exception as e: | |
| print(f"Error initializing ElevenLabs client: {e}") | |
| def text_to_speech(self, text: str, voice_id: Optional[str] = None) -> Optional[bytes]: | |
| """ | |
| Convert text to speech | |
| Args: | |
| text: Text to convert | |
| voice_id: Optional voice ID override | |
| Returns: | |
| Audio bytes or None if error | |
| """ | |
| if not self.client: | |
| print("ElevenLabs client not initialized. Please check API key.") | |
| return None | |
| try: | |
| voice_to_use = voice_id or self.voice_id | |
| # Generate audio using newer model (free tier compatible) | |
| audio = self.client.generate( | |
| text=text, | |
| voice=voice_to_use, | |
| model="eleven_turbo_v2_5" # Free tier compatible model | |
| ) | |
| # Convert generator to bytes | |
| audio_bytes = b"" | |
| for chunk in audio: | |
| audio_bytes += chunk | |
| return audio_bytes | |
| except Exception as e: | |
| print(f"Error generating speech: {e}") | |
| return None | |
| def text_to_speech_stream(self, text: str, voice_id: Optional[str] = None): | |
| """ | |
| Convert text to speech with streaming | |
| Args: | |
| text: Text to convert | |
| voice_id: Optional voice ID override | |
| Yields: | |
| Audio chunks | |
| """ | |
| if not self.client: | |
| print("ElevenLabs client not initialized. Please check API key.") | |
| return | |
| try: | |
| voice_to_use = voice_id or self.voice_id | |
| # Stream audio using newer model (free tier compatible) | |
| audio_stream = self.client.generate( | |
| text=text, | |
| voice=voice_to_use, | |
| model="eleven_turbo_v2_5", | |
| stream=True | |
| ) | |
| for chunk in audio_stream: | |
| yield chunk | |
| except Exception as e: | |
| print(f"Error streaming speech: {e}") | |
| return | |
| def save_audio(self, audio_bytes: bytes, filename: str) -> bool: | |
| """ | |
| Save audio bytes to file | |
| Args: | |
| audio_bytes: Audio data | |
| filename: Output filename | |
| Returns: | |
| Success status | |
| """ | |
| try: | |
| with open(filename, 'wb') as f: | |
| f.write(audio_bytes) | |
| return True | |
| except Exception as e: | |
| print(f"Error saving audio: {e}") | |
| return False | |
| def get_available_voices(self) -> List[Dict[str, str]]: | |
| """ | |
| Get list of available voices | |
| Returns: | |
| List of voice information | |
| """ | |
| if not self.client: | |
| return [] | |
| try: | |
| voices = self.client.voices.get_all() | |
| return [{"voice_id": v.voice_id, "name": v.name} for v in voices.voices] | |
| except Exception as e: | |
| print(f"Error getting voices: {e}") | |
| return [ | |
| {"voice_id": "21m00Tcm4TlvDq8ikWAM", "name": "Rachel (Default)"}, | |
| {"voice_id": "ErXwobaYiN019PkySvjV", "name": "Antoni"}, | |
| {"voice_id": "MF3mGyEYCl7XYWbV9V6O", "name": "Elli"}, | |
| ] | |