import gradio as gr import whisper import subprocess import os import uuid import wave import contextlib from validation import validate_transcription from transformers import pipeline # Load model NLP emosi emotion_model = pipeline("text-classification", model="cardiffnlp/twitter-roberta-base-emotion", top_k=1) # Cek ffmpeg def ffmpeg_available(): try: subprocess.run(["ffmpeg", "-version"], capture_output=True) return True except FileNotFoundError: return False # Load Whisper model = whisper.load_model("base") # Analisis emosi sederhana def detect_emotion(text): emotions = { "sedih": "sedih", "lelah": "lelah", "bingung": "bingung", "tidak semangat": "putus asa", "baik-baik saja": "positif" } for keyword, label in emotions.items(): if keyword in text.lower(): return label return "netral" # Respons chatbot berdasarkan emosi def generate_response(text, emotion): responses = { "sedih": "Terima kasih sudah berbagi. Apa yang membuatmu merasa sedih?", "lelah": "Kamu merasa lelah ya... Mau cerita lebih lanjut?", "bingung": "Wajar kok merasa bingung. Aku di sini untuk mendengarkan.", "putus asa": "Aku paham. Kadang kita butuh waktu untuk memulihkan diri.", "positif": "Senang mendengarnya. Kalau ada yang ingin dibicarakan, aku siap mendengarkan.", "netral": "Terima kasih sudah berbagi. Aku di sini kalau kamu ingin cerita lebih banyak." } return responses.get(emotion, responses["netral"]) # Durasi audio def get_audio_duration(audio_path): try: with contextlib.closing(wave.open(audio_path, 'r')) as f: frames = f.getnframes() rate = f.getframerate() duration = frames / float(rate) return round(duration, 2) except Exception: return None # Fungsi utama def transcribe_audio(audio_file): ffmpeg_ok = ffmpeg_available() file_ext = os.path.splitext(audio_file)[1].lower() safe_formats = [".wav", ".flac"] if not ffmpeg_ok and file_ext not in safe_formats: return ( f"⚠️ Format {file_ext} tidak didukung tanpa ffmpeg.", None, None, None, None ) duration = get_audio_duration(audio_file) if duration is None: return "❌ Gagal membaca durasi audio.", None, None, None, None if duration < 1.5: return f"⚠️ Audio terlalu pendek ({duration} detik).", None, None, None, None try: result = model.transcribe(audio_file) raw_text = result["text"] language = result["language"] # Validasi transkripsi is_valid, validated_text_or_msg = validate_transcription(raw_text) if not is_valid: return ( f"⚠️ Transkripsi tidak valid: {validated_text_or_msg}", raw_text, None, validated_text_or_msg, "tidak terdeteksi" ) # Simpan ke file file_id = str(uuid.uuid4())[:8] output_path = f"/tmp/transcript_{file_id}.txt" with open(output_path, "w", encoding="utf-8") as f: f.write(validated_text_or_msg) # Analisis emosi emotion = detect_emotion(validated_text_or_msg) # Respons chatbot response = generate_response(validated_text_or_msg, emotion) return ( f"✅ Transkripsi selesai. Bahasa: **{language}**, Durasi: **{duration} detik**", validated_text_or_msg, output_path, response, f"🧠 Emosi terdeteksi: **{emotion}**" ) except Exception as e: return f"❌ Gagal transkripsi: {str(e)}", None, None, None, None # UI dengan tema khas Indonesia with gr.Blocks(theme=gr.themes.Soft()) as demo: gr.Markdown("## 🗣️ Chatbot Suara Empatik untuk Skrining Awal Depresi") gr.Markdown( """ Rekam suara langsung dari mikrofon. Chatbot akan mengenali isi percakapan, menganalisis emosi, dan memberikan respons empatik. Cocok untuk skrining awal kondisi psikologis secara anonim. """ ) gr.Markdown( "⚠️ *Percakapan ini bersifat anonim dan hanya untuk keperluan skrining awal. " "Tidak menggantikan diagnosis profesional.*" ) with gr.Row(): audio_input = gr.Audio(source="microphone", type="filepath", label="🎙️ Rekam Suara") file_output = gr.File(label="📄 Unduh Transkripsi") with gr.Row(): transcript_output = gr.Textbox(label="📝 Hasil Transkripsi", lines=4) emotion_output = gr.Markdown(label="🔍 Analisis Emosi") response_output = gr.Textbox(label="💬 Respons Chatbot", lines=3) status_output = gr.Markdown(label="📌 Status") def chatbot_ui(audio): return transcribe_audio(audio) audio_input.change(fn=chatbot_ui, inputs=audio_input, outputs=[ status_output, transcript_output, file_output, response_output, emotion_output ]) demo.launch()