|
|
""" |
|
|
Gradio Voice Recording App for Komentle |
|
|
Records user voice and sends to FastAPI backend |
|
|
""" |
|
|
import gradio as gr |
|
|
import requests |
|
|
import uuid |
|
|
from datetime import datetime |
|
|
import os |
|
|
|
|
|
|
|
|
BACKEND_URL = os.getenv("BACKEND_URL") |
|
|
|
|
|
def process_voice(audio): |
|
|
""" |
|
|
Process recorded voice and send to backend |
|
|
|
|
|
Args: |
|
|
audio: tuple (sample_rate, audio_data) or file path |
|
|
|
|
|
Returns: |
|
|
dict: Response from backend with scores |
|
|
""" |
|
|
if audio is None: |
|
|
return { |
|
|
"status": "error", |
|
|
"message": "μμ±μ΄ λ
Ήμλμ§ μμμ΅λλ€." |
|
|
} |
|
|
|
|
|
try: |
|
|
|
|
|
session_id = str(uuid.uuid4()) |
|
|
today = datetime.now().strftime("%Y-%m-%d") |
|
|
|
|
|
|
|
|
files = { |
|
|
'audio': ('audio.wav', open(audio, 'rb'), 'audio/wav') |
|
|
} |
|
|
data = { |
|
|
'date': today, |
|
|
'session_id': session_id |
|
|
} |
|
|
|
|
|
|
|
|
response = requests.post( |
|
|
f"{BACKEND_URL}/api/analyze-voice", |
|
|
files=files, |
|
|
data=data, |
|
|
timeout=30 |
|
|
) |
|
|
|
|
|
if response.status_code == 200: |
|
|
result = response.json() |
|
|
return format_result(result) |
|
|
else: |
|
|
return { |
|
|
"status": "error", |
|
|
"message": f"λ°±μλ μ€λ₯: {response.status_code}" |
|
|
} |
|
|
|
|
|
except Exception as e: |
|
|
return { |
|
|
"status": "error", |
|
|
"message": f"μ€λ₯ λ°μ: {str(e)}" |
|
|
} |
|
|
|
|
|
def format_result(result): |
|
|
"""Format backend response for display""" |
|
|
if result.get("status") == "error": |
|
|
return f"β μ€λ₯: {result.get('message')}" |
|
|
|
|
|
category = result.get("category", "unknown") |
|
|
pitch = result.get("pitch", 0.0) |
|
|
rhythm = result.get("rhythm", 0.0) |
|
|
energy = result.get("energy", 0.0) |
|
|
pronunciation = result.get("pronunciation", 0.0) |
|
|
transcript = result.get("transcript", 0.0) |
|
|
overall = result.get("overall", 0.0) |
|
|
advice = result.get("advice", "") |
|
|
is_correct = result.get("is_correct", False) |
|
|
|
|
|
output = f"π― μΉ΄ν
κ³ λ¦¬: {category}\n\n" |
|
|
|
|
|
|
|
|
if is_correct: |
|
|
output += "π μ λ΅μ
λλ€! μΆνν©λλ€!\n\n" |
|
|
else: |
|
|
output += "β μμ§ μ λ΅μ΄ μλλλ€. λ€μ λμ ν΄λ³΄μΈμ!\n\n" |
|
|
|
|
|
output += "π λΆμ κ²°κ³Ό:\n" |
|
|
output += f" - μλμ΄ (Pitch): {pitch:.1f}/100\n" |
|
|
output += f" - 리λ¬κ° (Rhythm): {rhythm:.1f}/100\n" |
|
|
output += f" - μλμ§ (Energy): {energy:.1f}/100\n" |
|
|
output += f" - λ°μ (Pronunciation): {pronunciation:.1f}/100\n" |
|
|
output += f" - λμ¬ μ νλ (Transcript): {transcript:.1f}/100\n" |
|
|
output += f"\nβ μ΄μ (Overall): {overall:.1f}/100\n" |
|
|
|
|
|
|
|
|
if advice: |
|
|
output += f"\nπ‘ AI μ‘°μΈ:\n{advice}\n" |
|
|
|
|
|
return output |
|
|
|
|
|
|
|
|
with gr.Blocks(title="π€ Komentle Voice Challenge") as demo: |
|
|
gr.Markdown("# π€ Komentle Voice Challenge") |
|
|
gr.Markdown("μ€λμ λ¬Έμ λ₯Ό μμ±μΌλ‘ λμ νμΈμ!") |
|
|
|
|
|
gr.Markdown("### μ¬μ© λ°©λ²") |
|
|
gr.Markdown(""" |
|
|
1. ποΈ λ§μ΄ν¬ λ²νΌμ ν΄λ¦νμ¬ λ
Ήμ μμ |
|
|
2. μ€λμ λ¬Έμ λ₯Ό μμ±μΌλ‘ λ§νκΈ° |
|
|
3. λ
Ήμ μλ£ ν 'λΆμ μμ' λ²νΌ ν΄λ¦ |
|
|
4. AIκ° λΆμν μ μ νμΈ |
|
|
""") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
audio_input = gr.Audio( |
|
|
sources=["microphone"], |
|
|
type="filepath", |
|
|
label="ποΈ μμ± λ
Ήμ", |
|
|
format="wav" |
|
|
) |
|
|
submit_btn = gr.Button("λΆμ μμ", variant="primary", size="lg") |
|
|
|
|
|
with gr.Column(): |
|
|
result_output = gr.Textbox( |
|
|
label="π λΆμ κ²°κ³Ό", |
|
|
lines=10, |
|
|
interactive=False |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
submit_btn.click( |
|
|
fn=process_voice, |
|
|
inputs=audio_input, |
|
|
outputs=result_output |
|
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch( |
|
|
server_name="0.0.0.0", |
|
|
server_port=7860, |
|
|
share=False |
|
|
) |
|
|
|