Spaces:
Sleeping
Sleeping
| # male_voice.py | |
| import pyttsx3 | |
| from pathlib import Path | |
| import asyncio | |
| from typing import Union | |
| def text_to_speech(text: str, output_path: Union[str, Path]) -> bool: | |
| """ | |
| Convert text to speech and save to file using pyttsx3. | |
| Returns True on success. | |
| """ | |
| try: | |
| engine = pyttsx3.init() | |
| engine.setProperty('rate', 150) # Speaking speed | |
| # Select male voice (prioritize David, then any non-Zira) | |
| voices = engine.getProperty('voices') | |
| selected = False | |
| for voice in voices: | |
| name = voice.name.lower() | |
| if 'david' in name or ('male' in name and 'zira' not in name): | |
| engine.setProperty('voice', voice.id) | |
| selected = True | |
| break | |
| if not selected and voices: | |
| engine.setProperty('voice', voices[0].id) # fallback | |
| # Ensure output path is Path object | |
| output_path = Path(output_path) | |
| output_path.parent.mkdir(parents=True, exist_ok=True) | |
| # Save to file | |
| engine.save_to_file(text, str(output_path)) | |
| engine.runAndWait() | |
| print(f"[pyttsx3] Saved: {output_path}") | |
| return True | |
| except Exception as e: | |
| print(f"[pyttsx3] Error: {e}") | |
| return False | |
| async def text_to_speech_async(text: str, output_path: Union[str, Path]) -> bool: | |
| """ | |
| Async wrapper for text_to_speech using asyncio.to_thread. | |
| Prevents blocking the Streamlit event loop. | |
| """ | |
| return await asyncio.to_thread(text_to_speech, text, output_path) |