Spaces:
Sleeping
Sleeping
| import os | |
| import time | |
| import tempfile | |
| import warnings | |
| import asyncio | |
| import edge_tts | |
| from transformers import pipeline | |
| # ------------------------------------------------------------------------- | |
| # Speech-to-Text (STT): Whisper-small running locally on CPU | |
| # - ~242MB model, good Hindi accuracy, no API dependency | |
| # ------------------------------------------------------------------------- | |
| _stt_pipeline = None | |
| def _get_stt_pipeline(): | |
| """Lazily loads the Whisper-small pipeline (only once on first call).""" | |
| global _stt_pipeline | |
| if _stt_pipeline is None: | |
| print("Loading Whisper-small model for STT (this takes ~30s on first run)...") | |
| # Suppress the duplicate logits-processor warning that appears | |
| # when language/task are passed alongside the model's own processor setup. | |
| with warnings.catch_warnings(): | |
| warnings.filterwarnings( | |
| "ignore", | |
| message="A custom logits processor of type", | |
| category=UserWarning, | |
| ) | |
| _stt_pipeline = pipeline( | |
| task="automatic-speech-recognition", | |
| model="openai/whisper-small", | |
| device="cpu", | |
| generate_kwargs={"language": "hindi", "task": "transcribe"}, | |
| ) | |
| print("Whisper-small ready.") | |
| return _stt_pipeline | |
| def speech_to_text(audio_filepath: str) -> str: | |
| """ | |
| Transcribes a Hindi audio recording into Hindi text (Devanagari). | |
| Args: | |
| audio_filepath: Path to the audio file (wav/mp3/webm/ogg). | |
| Returns: | |
| Transcribed Hindi string, or an error message beginning with 'Error:'. | |
| """ | |
| if not audio_filepath: | |
| return "" | |
| if not os.path.exists(audio_filepath): | |
| return f"Error: audio file not found at {audio_filepath}" | |
| try: | |
| pipe = _get_stt_pipeline() | |
| with warnings.catch_warnings(): | |
| warnings.filterwarnings( | |
| "ignore", | |
| message="A custom logits processor of type", | |
| category=UserWarning, | |
| ) | |
| result = pipe(audio_filepath) | |
| transcript = result.get("text", "").strip() | |
| if not transcript: | |
| return "Error: Could not transcribe audio - please speak clearly and try again." | |
| return transcript | |
| except Exception as exc: | |
| print(f"[STT Error] {exc}") | |
| return f"Error: Could not transcribe audio ({exc})" | |
| # ------------------------------------------------------------------------- | |
| # Text-to-Speech (TTS): Edge-TTS – high-quality neural Hindi voice | |
| # - Saves to /tmp/ so it works on read-only deployments like HF Spaces | |
| # ------------------------------------------------------------------------- | |
| async def _async_tts(text: str, output_path: str): | |
| """Async helper to run the Edge TTS communication.""" | |
| communicate = edge_tts.Communicate(text, "hi-IN-SwaraNeural") | |
| await communicate.save(output_path) | |
| def text_to_speech(text: str) -> str | None: | |
| """ | |
| Converts Hindi text to a high-quality spoken MP3 file using edge-tts. | |
| Saves into /tmp/ to ensure write access regardless of the deployment | |
| environment (HF Spaces /app directory is read-only). | |
| Args: | |
| text: Hindi text in Devanagari. | |
| Returns: | |
| Absolute path to the saved MP3 file, or None on failure. | |
| """ | |
| if not text or text.startswith("Error:"): | |
| return None | |
| max_retries = 3 | |
| for attempt in range(max_retries): | |
| try: | |
| # Use a named temp file so Gradio can serve it correctly | |
| tmp = tempfile.NamedTemporaryFile( | |
| suffix=".mp3", delete=False, dir="/tmp" | |
| ) | |
| output_path = tmp.name | |
| tmp.close() | |
| # Run the async edge-tts code synchronously | |
| asyncio.run(_async_tts(text, output_path)) | |
| return output_path | |
| except Exception as exc: | |
| err = str(exc) | |
| if "429" in err and attempt < max_retries - 1: | |
| wait = 2 ** attempt | |
| print(f"[TTS] Rate-limited, retrying in {wait}s... (attempt {attempt + 1}/{max_retries})") | |
| time.sleep(wait) | |
| else: | |
| print(f"[TTS Error] {exc}") | |
| return None | |