milicka's picture
Upload 3 files
2652037 verified
Raw
History Blame Contribute Delete
5.41 kB
"""Gradio web UI for the phonetic-reduction analyzer.
Run with:
.venv/bin/python app.py
Then open the URL Gradio prints (defaults to http://127.0.0.1:7860).
"""
from __future__ import annotations
import os
import tempfile
import gradio as gr
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import pandas as pd
from reduction import pipeline, stt, tts
# --- model cache ---------------------------------------------------------
_models: dict[str, object] = {}
_voice = None
def _get_model(size: str):
if size not in _models:
_models[size] = stt.load_model(size=size)
return _models[size]
def _get_voice():
global _voice
if _voice is None:
_voice = tts.load_voice()
return _voice
# --- plot ----------------------------------------------------------------
def _make_plot(df: pd.DataFrame) -> plt.Figure:
n = len(df)
fig, ax = plt.subplots(figsize=(max(8, n * 0.55), 4.5))
x = list(range(n))
labels = df["word"].tolist()
# Colour bars by STT confidence: low-confidence → red, normal → steelblue
probs = df["stt_probability"].fillna(1.0).tolist()
bar_colors = ["#cc3333" if p < 0.6 else "#4a90c4" for p in probs]
ax.bar(x, df["combined_score"], color=bar_colors, alpha=0.55, label="combined")
ax.plot(x, df["duration_score"], "o-", color="#2c7a2c", lw=1.4, label="duration", alpha=0.85)
ax.plot(x, df["spectral_score"], "s-", color="#d97726", lw=1.4, label="spectral", alpha=0.85)
ax.axhline(1.0, color="gray", linestyle="--", linewidth=0.8)
ax.set_xticks(x)
ax.set_xticklabels(labels, rotation=40, ha="right", fontsize=10)
ax.set_ylabel("skóre (1 = průměr, <1 redukováno)")
ax.set_xlabel("token")
ax.set_title("Per-word reduction scores")
ax.legend(loc="upper right", framealpha=0.9)
ax.grid(True, axis="y", alpha=0.25)
fig.tight_layout()
return fig
# --- main analyze callback ----------------------------------------------
def analyze_audio(audio_path: str | None, whisper_size: str, progress=gr.Progress()):
if not audio_path:
return "❌ Nahraj nebo nahrej zvuk.", None, None, None
progress(0.1, desc=f"Načítám Whisper {whisper_size}…")
model = _get_model(whisper_size)
progress(0.3, desc="Načítám Piper hlas…")
voice = _get_voice()
progress(0.5, desc="Analyzuji…")
rows, whisper_text, _ = pipeline.analyze(
audio_path, model, voice, language="cs", groundtruth=None, trim=True
)
progress(0.9, desc="Renderuji graf a CSV…")
df = pd.DataFrame(rows)
# Pretty display dataframe (round numerics, drop internal cols)
display_cols = [
"word",
"stt_probability",
"orig_duration",
"tts_duration",
"duration_ratio",
"spectral_distance",
"duration_score",
"spectral_score",
"combined_score",
]
display_df = df[display_cols].copy()
for c in display_cols[1:]:
display_df[c] = display_df[c].round(3)
fig = _make_plot(df)
# CSV download (full row, not abbreviated)
csv_path = os.path.join(tempfile.gettempdir(), "reduction_result.csv")
df.to_csv(csv_path, index=False)
return whisper_text or "(žádný transkript)", display_df, fig, csv_path
# --- UI -----------------------------------------------------------------
HEADER_MD = """
# Měření fonetické redukce — prototyp
Per-word reduction scores via STT → per-word TTS canonical → duration + spectral DTW.
Skóre **< 1** = redukováno oproti průměru nahrávky, **> 1** = plně artikulováno.
Červené sloupce = nízká STT confidence (pravděpodobně přeslech / halucinace).
"""
with gr.Blocks(title="Phonetic reduction analyzer") as demo:
gr.Markdown(HEADER_MD)
with gr.Row():
with gr.Column(scale=2):
audio_in = gr.Audio(
sources=["upload", "microphone"],
type="filepath",
label="Nahrávka (drag & drop wav, nebo klikni na mikrofon a namluv)",
)
with gr.Column(scale=1):
whisper_dd = gr.Dropdown(
choices=["small", "medium", "large-v3"],
value="large-v3",
label="Whisper model",
info="large-v3 = nejlepší kvalita, ~6s/věta na CPU",
)
run_btn = gr.Button("Analyzuj", variant="primary", size="lg")
transcript_out = gr.Textbox(label="Whisper transkript", interactive=False, lines=2)
plot_out = gr.Plot(label="Skóre redukce")
table_out = gr.Dataframe(label="Per-word detaily", interactive=False)
csv_out = gr.File(label="Stáhnout CSV", interactive=False)
run_btn.click(
analyze_audio,
inputs=[audio_in, whisper_dd],
outputs=[transcript_out, table_out, plot_out, csv_out],
)
if __name__ == "__main__":
# On HuggingFace Spaces, $SPACE_ID is set; on local dev it isn't.
on_hf = bool(os.environ.get("SPACE_ID"))
if on_hf:
# HF provides PORT and expects 0.0.0.0; let HF's launcher own model preloading
demo.launch(server_name="0.0.0.0")
else:
print("[startup] preloading large-v3 + Piper voice (one-time, ~10s)…")
_get_model("large-v3")
_get_voice()
print("[startup] ready, launching Gradio…")
demo.launch(server_name="127.0.0.1", server_port=7860, inbrowser=False)