Spaces:
Runtime error
Runtime error
File size: 5,321 Bytes
b97b788 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | """
speech.py — Whisper-based offline Speech-to-Text module
Uses OpenAI Whisper (tiny model) for offline speech recognition.
"""
import os
import tempfile
import traceback
from typing import Optional, Dict, Any
import whisper
from config import WHISPER_MODEL
class SpeechRecognizer:
"""Offline speech-to-text using OpenAI Whisper."""
def __init__(self):
self.model = None
self.model_name = WHISPER_MODEL
self.ready = False
def load(self):
"""Load the Whisper model."""
try:
print(f"[Speech] Loading Whisper '{self.model_name}' model...")
self.model = whisper.load_model(self.model_name)
self.ready = True
print(f"[Speech] Whisper '{self.model_name}' loaded successfully.")
except Exception as e:
print(f"[Speech] ERROR loading Whisper: {e}")
traceback.print_exc()
self.ready = False
def transcribe(self, audio_path: str, language: Optional[str] = None) -> Dict[str, Any]:
"""
Transcribe an audio file to text.
Args:
audio_path: Path to the audio file (WAV, MP3, etc.)
language: Optional ISO 639-1 language code to force.
If None, Whisper auto-detects the language.
Returns:
dict with keys:
- text: transcribed text
- language: detected/forced language code
- confidence: detection confidence (0-1)
- segments: list of timed segments
"""
if not self.ready or self.model is None:
return {
"text": "",
"language": None,
"confidence": 0.0,
"segments": [],
"error": "Whisper model not loaded"
}
try:
options = {}
if language:
options["language"] = language
result = self.model.transcribe(audio_path, **options)
# Extract language detection info
detected_lang = result.get("language", language or "en")
# Get detection confidence from the model
confidence = self._get_language_confidence(audio_path, detected_lang)
segments = []
for seg in result.get("segments", []):
segments.append({
"start": round(seg["start"], 2),
"end": round(seg["end"], 2),
"text": seg["text"].strip()
})
return {
"text": result["text"].strip(),
"language": detected_lang,
"confidence": confidence,
"segments": segments,
"error": None
}
except Exception as e:
print(f"[Speech] Transcription error: {e}")
traceback.print_exc()
return {
"text": "",
"language": None,
"confidence": 0.0,
"segments": [],
"error": str(e)
}
def transcribe_bytes(self, audio_bytes: bytes, language: Optional[str] = None) -> Dict[str, Any]:
"""Transcribe raw audio bytes by saving to a temp file first."""
tmp_path = None
try:
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp:
tmp.write(audio_bytes)
tmp_path = tmp.name
return self.transcribe(tmp_path, language)
finally:
if tmp_path and os.path.exists(tmp_path):
try:
os.unlink(tmp_path)
except OSError:
pass
def detect_language(self, audio_path: str) -> Dict[str, Any]:
"""
Detect the spoken language in an audio file without full transcription.
Returns:
dict with language code and confidence scores
"""
if not self.ready or self.model is None:
return {"language": None, "confidence": 0.0, "error": "Model not loaded"}
try:
audio = whisper.load_audio(audio_path)
audio = whisper.pad_or_trim(audio)
mel = whisper.log_mel_spectrogram(audio).to(self.model.device)
_, probs = self.model.detect_language(mel)
# Get top 5 languages
sorted_langs = sorted(probs.items(), key=lambda x: x[1], reverse=True)
top_lang = sorted_langs[0]
return {
"language": top_lang[0],
"confidence": round(top_lang[1], 4),
"top_5": {k: round(v, 4) for k, v in sorted_langs[:5]},
"error": None
}
except Exception as e:
return {"language": None, "confidence": 0.0, "error": str(e)}
def _get_language_confidence(self, audio_path: str, detected_lang: str) -> float:
"""Get confidence score for a detected language."""
try:
audio = whisper.load_audio(audio_path)
audio = whisper.pad_or_trim(audio)
mel = whisper.log_mel_spectrogram(audio).to(self.model.device)
_, probs = self.model.detect_language(mel)
return round(probs.get(detected_lang, 0.0), 4)
except Exception:
return 0.0
|