speech2Text / app.py
Murali Chandran
fixed app
866375a
Raw History Blame Contribute Delete
3.46 kB
#!/usr/bin/env python3
# ------------------------------------------------------------
# Whisper Small LS-100 Demo (local or Hugging Face Spaces)
# Author: Murali Chandran (codeninja3d)
# ------------------------------------------------------------
import torch
import torchaudio
import gradio as gr
from transformers import WhisperProcessor, WhisperForConditionalGeneration
# -------------------------------
# Config
# -------------------------------
MODEL_ID = "codeninja3d/whisper-small-ls100"
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
# print(f"βœ… Loading model from: {MODEL_ID}")
processor = WhisperProcessor.from_pretrained(MODEL_ID)
model = WhisperForConditionalGeneration.from_pretrained(MODEL_ID).to(DEVICE).eval()
# print(f"βœ… #Params: {sum(p.numel() for p in model.parameters()) / 1e6:.2f}M")
# print("βœ… Sample rate:", processor.feature_extractor.sampling_rate)
# -------------------------------
# Transcribe function
# -------------------------------
def transcribe(audio, language, task):
if audio is None:
return "⚠️ Please record or upload an audio clip."
# Handle tuple (mic) or file path (upload)
if isinstance(audio, tuple):
sr, waveform_np = audio
waveform = torch.tensor(waveform_np, dtype=torch.float32)
if waveform.ndim > 1: # [samples, channels] β†’ [1, samples]
waveform = waveform.mean(dim=-1, keepdim=True).T
else:
waveform, sr = torchaudio.load(audio)
# Ensure mono and correct shape [1, samples]
if waveform.ndim > 1:
waveform = waveform.mean(dim=0, keepdim=True)
# Resample to 16 kHz
if sr != 16000:
waveform = torchaudio.functional.resample(waveform, sr, 16000)
sr = 16000
# βœ… Normalize amplitude to [-1, 1]
waveform = waveform / waveform.abs().max()
# βœ… Force 1D numpy array (no 2D padding issues)
waveform_np = waveform.squeeze().numpy().astype("float32")
# Preprocess
inputs = processor.feature_extractor(
waveform_np,
sampling_rate=sr,
return_tensors="pt"
).to(DEVICE)
# print(f"[DEBUG] waveform shape: {waveform.shape}, dtype: {waveform.dtype}, sr={sr}")
# print(f"[DEBUG] min={waveform.min().item():.3f}, max={waveform.max().item():.3f}, mean={waveform.mean().item():.3f}")
# Decoder setup
forced_decoder_ids = processor.get_decoder_prompt_ids(language="en", task="transcribe")
# print("[DEBUG] forced_decoder_ids:", forced_decoder_ids)
with torch.no_grad():
pred_ids = model.generate(
**inputs,
forced_decoder_ids=forced_decoder_ids,
max_new_tokens=256,
num_beams=5,
no_repeat_ngram_size=3,
repetition_penalty=1.2,
early_stopping=True
)
text = processor.tokenizer.batch_decode(pred_ids, skip_special_tokens=True)[0]
return text.strip()
# -------------------------------
# Gradio Interface
# -------------------------------
iface = gr.Interface(
fn=transcribe,
inputs=[
gr.Audio(type="numpy", label="πŸŽ™οΈ Record or Upload Audio"),
],
outputs=gr.Textbox(label="πŸ“„ Transcription"),
title="Whisper Small LS-100 (Fine-tuned)",
description="Upload or record audio to test Murali's fine-tuned Whisper model on LibriSpeech-100.",
theme="soft",
allow_flagging="auto"
)
iface.launch(server_name="0.0.0.0", server_port=7860)