import gradio as gr
from transformers import pipeline
import re
import time
# Load both ASR models
whisper_asr = pipeline("automatic-speech-recognition", model="1morecupofhottea/Whisper-Code-Switching-Kh-En")
wav2vec_asr = pipeline("automatic-speech-recognition", model="1morecupofhottea/wav2vec2-Code-Switching-Kh_En")
def clean_transcript(text: str) -> str:
# Remove , , , , etc.
return re.sub(r"?[^>]+>", "", text).strip()
def transcribe(audio, model_choice):
if audio is None:
return "Please upload or record an audio file first."
try:
if model_choice == "Whisper Model":
result = whisper_asr(audio)
return f"Transcription Complete:\n\n{result['text']}"
else:
result = wav2vec_asr(audio)
cleaned_text = clean_transcript(result["text"])
return f"Transcription Complete:\n\n{cleaned_text}"
except Exception as e:
return f"Error during transcription: {str(e)}"
def clear_all():
return None, "Whisper Model", ""
# Custom CSS with dark theme
custom_css = """
/*
DARK THEME COLOR PALETTE:
- Background: #000000 (Pure black - main background)
- Surface Dark: #1a1a1a (Dark gray - cards, containers)
- Surface Medium: #2d2d2d (Medium gray - inputs, components)
- Border: #404040 (Gray - borders, dividers)
- Text Primary: #ffffff (White - main text, headers)
- Text Secondary: #b0b0b0 (Light gray - secondary text)
- Accent: #4a9eff (Blue - buttons, highlights)
- Accent Hover: #357abd (Darker blue - hover states)
- Success: #28a745 (Green - success states)
- Warning: #ffc107 (Yellow - warnings)
*/
/* Global Styles */
.gradio-container {
background: #000000 !important;
font-family: 'Georgia', 'Times New Roman', serif !important;
color: #ffffff !important;
min-height: 100vh !important;
}
/* Header Styling */
.header-section {
background: #1a1a1a;
border: 2px solid #404040;
border-radius: 12px;
padding: 30px;
margin-bottom: 25px;
box-shadow: 0 4px 12px rgba(255, 255, 255, 0.1);
}
.header-section h1 {
color: #ffffff !important;
font-size: 2.4em !important;
font-weight: 700 !important;
text-align: center;
margin-bottom: 15px;
border-bottom: 3px solid #4a9eff;
padding-bottom: 15px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}
.header-section p {
color: #b0b0b0;
font-size: 1.1em;
text-align: center;
margin: 8px 0;
line-height: 1.6;
}
/* Main Content Cards */
.input-card, .output-card {
background: #1a1a1a;
border: 2px solid #404040;
border-radius: 12px;
padding: 25px;
box-shadow: 0 4px 12px rgba(255, 255, 255, 0.05);
margin-bottom: 20px;
}
/* Button Styling */
.primary-button {
background: linear-gradient(135deg, #4a9eff, #357abd) !important;
border: 2px solid #4a9eff !important;
border-radius: 8px !important;
padding: 14px 28px !important;
font-size: 1.1em !important;
font-weight: 600 !important;
color: #ffffff !important;
transition: all 0.3s ease !important;
box-shadow: 0 4px 12px rgba(74, 158, 255, 0.3) !important;
}
.primary-button:hover {
background: linear-gradient(135deg, #357abd, #2a5d8f) !important;
border-color: #357abd !important;
transform: translateY(-2px) !important;
box-shadow: 0 6px 16px rgba(74, 158, 255, 0.4) !important;
}
.secondary-button {
background: #2d2d2d !important;
border: 2px solid #404040 !important;
border-radius: 8px !important;
padding: 12px 24px !important;
font-size: 1em !important;
font-weight: 500 !important;
color: #ffffff !important;
transition: all 0.3s ease !important;
}
.secondary-button:hover {
background: #404040 !important;
border-color: #555555 !important;
transform: translateY(-1px) !important;
}
/* Component Styling */
.audio-component, .dropdown-component {
border-radius: 8px !important;
border: 2px solid #404040 !important;
background: #2d2d2d !important;
color: #ffffff !important;
}
/* Text Styling */
.output-text {
background: #2d2d2d !important;
border-radius: 8px !important;
border: 2px solid #404040 !important;
padding: 20px !important;
font-size: 1.05em !important;
line-height: 1.7 !important;
font-family: 'Consolas', 'Courier New', monospace !important;
color: #ffffff !important;
}
/* Features Section */
.features-section {
background: #1a1a1a;
border: 2px solid #404040;
border-radius: 12px;
padding: 25px;
margin-top: 25px;
box-shadow: 0 4px 12px rgba(255, 255, 255, 0.05);
}
.feature-item {
margin-bottom: 15px;
color: #b0b0b0;
font-size: 1.05em;
line-height: 1.6;
padding: 10px;
background: #2d2d2d;
border-radius: 6px;
border-left: 4px solid #4a9eff;
}
.feature-item strong {
color: #ffffff;
}
/* Typography */
h3 {
font-family: 'Georgia', 'Times New Roman', serif !important;
color: #ffffff !important;
font-weight: 600 !important;
margin-bottom: 15px !important;
}
/* Component Overrides */
label {
color: #ffffff !important;
font-weight: 500 !important;
font-family: 'Georgia', 'Times New Roman', serif !important;
}
::placeholder {
color: #808080 !important;
opacity: 0.8;
}
*:focus {
outline: 2px solid #4a9eff !important;
outline-offset: 2px !important;
}
/* Gradio Specific Overrides */
.gradio-textbox textarea,
.gradio-dropdown,
.gradio-audio {
border: 2px solid #404040 !important;
border-radius: 8px !important;
background: #2d2d2d !important;
color: #ffffff !important;
font-family: inherit !important;
}
.gradio-textbox textarea:focus,
.gradio-dropdown:focus,
.gradio-audio:focus {
border-color: #4a9eff !important;
box-shadow: 0 0 0 3px rgba(74, 158, 255, 0.2) !important;
}
/* Scrollbar Styling */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #1a1a1a;
}
::-webkit-scrollbar-thumb {
background: #404040;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #4a9eff;
}
/* Selection Styling */
::selection {
background: #4a9eff;
color: #ffffff;
}
/* Responsive Design */
@media (max-width: 768px) {
.header-section h1 {
font-size: 2em !important;
}
.input-card, .output-card {
padding: 20px;
margin-bottom: 15px;
}
.primary-button, .secondary-button {
padding: 12px 20px !important;
font-size: 0.95em !important;
}
}
/* Animation for better UX */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.input-card, .output-card, .features-section, .header-section {
animation: fadeIn 0.5s ease-out;
}
"""
# Create the main interface
with gr.Blocks(css=custom_css, title="CS-ASR | Code-Switching Speech Recognition") as demo:
# Header Section
with gr.Column(elem_classes="header-section"):
gr.HTML("""
Code-Switching ASR Platform
Speech Recognition for Khmer-English Code-Switching
""")
# Main Content
with gr.Row():
# Input Section
with gr.Column(scale=1, elem_classes="input-card"):
gr.HTML("Audio Input
")
audio_input = gr.Audio(
sources=["microphone", "upload"],
type="filepath",
label="Record or Upload Audio",
elem_classes="audio-component"
)
model_selector = gr.Dropdown(
choices=[
"Whisper Model",
"Wav2Vec2 Model"
],
value="Whisper Model",
label="Select AI Model",
elem_classes="dropdown-component",
info="Choose the model that best fits your needs"
)
# Action Buttons
with gr.Row():
transcribe_button = gr.Button(
"Start Transcription",
variant="primary",
elem_classes="primary-button",
scale=2
)
clear_button = gr.Button(
"Clear All",
elem_classes="secondary-button",
scale=1
)
# Output Section
with gr.Column(scale=1, elem_classes="output-card"):
gr.HTML("Transcription Result
")
output_text = gr.Textbox(
label="Your Transcription Will Appear Here",
placeholder="Upload an audio file and click 'Start Transcription' to begin processing.",
lines=12,
elem_classes="output-text",
interactive=False
)
# Event Handlers
transcribe_button.click(
fn=transcribe,
inputs=[audio_input, model_selector],
outputs=output_text,
show_progress=True
)
clear_button.click(
fn=clear_all,
outputs=[audio_input, model_selector, output_text]
)
# Auto-transcribe when audio is uploaded (optional)
audio_input.change(
fn=lambda audio, model: transcribe(audio, model) if audio is not None else "",
inputs=[audio_input, model_selector],
outputs=output_text,
show_progress=True
)
# Launch with custom configuration
if __name__ == "__main__":
demo.launch()