import os
import base64
import gradio as gr
from pathlib import Path
from constants import STREAM_EVERY, APP_TMP_DIR, MODEL_ID, VAD_MODEL_ID
from ui import LED_DOT_OFF
from hf_dataset_utils import ALL_FILES
from stream_pipeline import (
on_start_recording,
on_stop_recording,
shutdown_streamers,
stream_step,
)
from offline_pipeline import (
load_file_from_dataset,
load_local_file,
run_offline_pipeline,
)
from clean_up import purge_tmp_directory, cleanup_previous_run
_CSS_DIR = Path(__file__).resolve().parent / "assets"
# Embed the logo as a self-contained data URI so it never depends on an
# external CDN URL (the previous Mintlify link expired and broke the header).
_LOGO_B64 = base64.b64encode((_CSS_DIR / "logo.svg").read_bytes()).decode("ascii")
_LOGO_DATA_URI = f"data:image/svg+xml;base64,{_LOGO_B64}"
# Model identifiers for a subtle footer (short slugs straight from constants).
_MODEL_FOOTER = (
'
'
f"Voice Focus: {MODEL_ID}"
f" · VAD: {VAD_MODEL_ID}"
"
"
)
with gr.Blocks() as demo:
sample_stem = gr.State("")
last_sample_stem = gr.State("")
input_array = gr.State()
streaming_sr = gr.State(None)
current_sample_rate = gr.State(None)
gr.HTML(
''
f'
'
""
)
gr.Markdown(open("docs/intro.md", "r", encoding="utf-8").read())
# ✅ Global controls (shared by both tabs)
stt_model = gr.Radio(
label="STT Model",
choices=["Soniox STT-RT v3", "Deepgram Nova-3 RT"],
value="Soniox STT-RT v3",
interactive=True,
)
# Streamers are created on first start_recording to avoid Soniox "No audio received" timeout
enhancement_level = gr.Slider(
minimum=0,
maximum=100,
step=1,
value=80,
label="Enhancement level (%)",
scale=2,
)
with gr.Tabs():
with gr.Tab("Stream audio in real time") as stream_tab:
with gr.Row(equal_height=False, elem_classes="stream-layout"):
with gr.Column(scale=4, min_width=320):
with gr.Group(elem_classes="panel section-panel"):
gr.Markdown("### Input", elem_classes="title")
gr.Markdown(
open("docs/online.md", "r", encoding="utf-8").read(),
elem_classes="tab-description",
)
input_gain_db = gr.Slider(
minimum=0,
maximum=20,
step=0.5,
value=0,
label="Input gain (dB)",
)
audio_stream = gr.Audio(
sources=["microphone"],
streaming=True,
elem_id="audio_stream",
)
with gr.Column(scale=6, min_width=420):
with gr.Group(elem_classes="status-panel output-panel"):
gr.Markdown("### Live Output", elem_classes="title")
with gr.Row(
equal_height=True, elem_classes="status-indicators"
):
with gr.Group(elem_classes="status-card"):
gr.Markdown(
"**System Status**",
elem_classes="status-card__label",
)
system_status_led = gr.HTML(
value=LED_DOT_OFF, show_label=False
)
system_status_text = gr.Markdown(
value="Off",
elem_classes="status-card__subtext",
)
with gr.Group(elem_classes="status-card"):
gr.Markdown(
"**Voice Activity**",
elem_classes="status-card__label",
)
vad_led = gr.HTML(value=LED_DOT_OFF, show_label=False)
with gr.Row(
equal_height=True,
elem_classes="transcript-row transcript-row--large",
):
enhanced_text = gr.Textbox(
label="Enhanced Transcript",
lines=10,
autoscroll=False,
)
raw_text = gr.Textbox(
label="Raw Transcript",
lines=10,
autoscroll=False,
)
with gr.Tab("Pick Example") as dataset_tab:
with gr.Group(elem_classes="panel section-panel"):
gr.Markdown("### Input", elem_classes="title")
gr.Markdown(
open("docs/example_pick.md", "r", encoding="utf-8").read(),
elem_classes="tab-description",
)
dataset_dropdown = gr.Dropdown(
choices=ALL_FILES, value=ALL_FILES[0], label="Sample"
)
audio_file_from_dataset = gr.Audio(
label="Preview",
autoplay=False,
interactive=False,
)
with gr.Tab("Upload local file") as upload_tab:
with gr.Group(elem_classes="panel section-panel"):
gr.Markdown("### Input", elem_classes="title")
gr.Markdown(
open("docs/local_file.md", "r", encoding="utf-8").read(),
elem_classes="tab-description",
)
audio_file_upload = gr.File(
file_types=[".wav", ".mp3", ".flac", ".m4a", ".ogg"],
file_count="single",
scale=3,
)
normalize = gr.Checkbox(label="Normalize audio", value=False)
audio_preview = gr.Audio(
label="Preview",
autoplay=False,
interactive=False,
)
enhance_btn = gr.Button("Enhance with Quail Voice Focus", scale=2, visible=False)
with gr.Group(elem_classes="panel results-card", visible=False) as results_card:
result_title = gr.Markdown("", elem_classes="title")
enhanced_audio = gr.Audio(
type="numpy",
interactive=False,
buttons=["download"],
autoplay=False,
)
with gr.Row(equal_height=True, elem_classes="results-row"):
with gr.Column(scale=5, min_width=320, elem_classes="results-left"):
noisy_image = gr.Image(
label="Input spectrogram", format="png", type="filepath"
)
enhanced_image = gr.Image(
label="Enhanced spectrogram", format="png", type="filepath"
)
with gr.Column(scale=5, min_width=320, elem_classes="results-right"):
original_transcript = gr.Textbox(
label="Groundtruth transcription", lines=3, interactive=False
)
noisy_transcript = gr.Textbox(
label="Noisy transcription", lines=3, interactive=False
)
enhanced_transcript = gr.Textbox(
label="Transcription post enhancement", lines=3, interactive=False
)
gr.HTML(_MODEL_FOOTER)
# ------------------------------------------------------
# TAB CHANGES
# ------------------------------------------------------
stream_tab.select(
lambda: (
gr.update(visible=False),
gr.update(visible=False),
gr.update(sources=["microphone"], streaming=True, interactive=True),
),
outputs=[results_card, enhance_btn, audio_stream],
)
def _on_not_streaming_tab():
shutdown_streamers()
return (
gr.update(streaming=False, interactive=False),
gr.update(visible=True),
LED_DOT_OFF,
LED_DOT_OFF,
"Off",
)
upload_tab.select(
_on_not_streaming_tab,
outputs=[
audio_stream,
enhance_btn,
vad_led,
system_status_led,
system_status_text,
],
).then(
load_local_file,
inputs=[audio_file_upload, normalize],
outputs=[input_array, sample_stem, audio_preview, current_sample_rate],
)
dataset_tab.select(
_on_not_streaming_tab,
outputs=[
audio_stream,
enhance_btn,
vad_led,
system_status_led,
system_status_text,
],
).then(
load_file_from_dataset,
inputs=dataset_dropdown,
outputs=[
audio_file_from_dataset,
input_array,
sample_stem,
current_sample_rate,
],
)
# ------------------------------------------------------
# STREAMING EVENTS
# ------------------------------------------------------
audio_stream.stream(
stream_step,
inputs=[
audio_stream,
streaming_sr,
stt_model,
enhancement_level,
input_gain_db,
],
outputs=[
streaming_sr,
system_status_led,
system_status_text,
enhanced_text,
raw_text,
vad_led,
],
stream_every=STREAM_EVERY,
time_limit=60 * 2,
concurrency_limit=1,
)
audio_stream.stop_recording(
on_stop_recording,
outputs=[vad_led, system_status_led, system_status_text, streaming_sr],
).then(
shutdown_streamers,
)
audio_stream.start_recording(
on_start_recording,
outputs=[enhanced_text, raw_text, system_status_led, system_status_text],
)
# ------------------------------------------------------
# OFFLINE EVENTS (DATASET + LOCAL FILE)
# ------------------------------------------------------
# Dataset dropdown selection triggers loading the audio file and hiding results until enhancement
dataset_dropdown.change(
lambda: gr.update(visible=False),
outputs=results_card,
).then(
load_file_from_dataset,
inputs=dataset_dropdown,
outputs=[
audio_file_from_dataset,
input_array,
sample_stem,
current_sample_rate,
],
)
# Uploading a local file triggers loading the audio file and hiding results until enhancement
audio_file_upload.change(
lambda: gr.update(visible=False),
outputs=results_card,
).then(
load_local_file,
inputs=[audio_file_upload, normalize],
outputs=[input_array, sample_stem, audio_preview, current_sample_rate],
)
normalize.change(
load_local_file,
inputs=[audio_file_upload, normalize],
outputs=[input_array, sample_stem, audio_preview, current_sample_rate],
)
# Enhancement button: run pipeline with live transcript progress (dataset + local file modes).
enhance_btn.click(cleanup_previous_run, inputs=[last_sample_stem]).then(
lambda: gr.update(visible=True),
outputs=results_card,
).then(
run_offline_pipeline,
inputs=[
input_array,
current_sample_rate,
enhancement_level,
stt_model,
sample_stem,
],
outputs=[
enhanced_audio,
enhanced_image,
noisy_image,
original_transcript,
noisy_transcript,
enhanced_transcript,
last_sample_stem,
],
).failure(
lambda: gr.Warning(
"Enhancement failed. Please refresh page and make sure you have a stable connection."
)
)
os.makedirs(APP_TMP_DIR, exist_ok=True)
purge_tmp_directory(max_age_minutes=0, tmp_dir=APP_TMP_DIR)
demo.queue()
demo.launch(
css=(_CSS_DIR / "styling.css").read_text(encoding="utf-8"),
allowed_paths=[APP_TMP_DIR],
)