Yuo / app.py
don0726's picture
Update app.py
cc0bb5f verified
Raw
History Blame Contribute Delete
2.15 kB
import gradio as gr
import whisperx
import torch
import json
import numpy as np
device = "cuda" if torch.cuda.is_available() else "cpu"
def align_words(audio_file, transcript):
audio = whisperx.load_audio(audio_file)
# ── Trim leading silence ──────────────────────────────────────────────────
FRAME = 160 # 10ms at 16kHz
THRESH = 0.001 # RMS energy threshold
speech_start = 0
for i in range(0, len(audio) - FRAME, FRAME):
if float(np.sqrt(np.mean(audio[i:i+FRAME]**2))) > THRESH:
speech_start = i
break
audio_trimmed = np.ascontiguousarray(audio[speech_start:], dtype=np.float32)
# ── Forced alignment ──────────────────────────────────────────────────────
segments = [{"text": transcript, "start": 0, "end": 9999}]
model_a, metadata = whisperx.load_align_model(language_code="en", device=device)
result = whisperx.align(segments, model_a, metadata, audio_trimmed, device)
# ── Build output ──────────────────────────────────────────────────────────
output = []
for w in result["word_segments"]:
start = w["start"]
end = w["end"]
# WhisperX sometimes returns ms instead of seconds β€” normalise
if start > 500:
start = round(start / 1000, 3)
end = round(end / 1000, 3)
else:
start = round(start, 3)
end = round(end, 3)
output.append({"word": w["word"], "start": start, "end": end})
return json.dumps(output, indent=2)
demo = gr.Interface(
fn=align_words,
inputs=[
gr.Audio(type="filepath", label="Audio"),
gr.Textbox(lines=10, label="Exact Transcript")
],
outputs=gr.Textbox(label="Word Timestamps JSON"),
title="Fast Word Timestamp Alignment"
)
demo.launch()