test1 / src /audio.py
Aniket Sirsikar
perf: implement benchmark-driven optimizations
c13b073
Raw
History Blame Contribute Delete
4.28 kB
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