Spaces:
Sleeping
Sleeping
| """ | |
| Loop Mind — DAM Voice Biomarker Inference Space | |
| ================================================ | |
| Wraps KintsugiHealth/DAM for voice biomarker analysis. | |
| Accepts audio, returns depression/anxiety severity scores. | |
| Deployed as a Hugging Face Space (Gradio). | |
| Called by the Loop Mind /voice-biomarker Supabase Edge Function. | |
| """ | |
| import sys | |
| import os | |
| import subprocess | |
| import warnings | |
| import json | |
| import gradio as gr | |
| import torch | |
| import torchaudio | |
| warnings.filterwarnings("ignore") | |
| # Download the DAM model on first run | |
| if not os.path.exists("dam"): | |
| print("Downloading KintsugiHealth/DAM model (~1GB)...") | |
| subprocess.run(["git", "clone", "https://huggingface.co/KintsugiHealth/dam"]) | |
| sys.path.append(os.path.abspath("dam")) | |
| print("Loading DAM pipeline...") | |
| try: | |
| from pipeline import Pipeline | |
| dam_pipeline = Pipeline() | |
| print("DAM model loaded successfully.") | |
| except Exception as e: | |
| print(f"Failed to load DAM model: {e}") | |
| dam_pipeline = None | |
| DEP_LABELS = {0: "none", 1: "mild-moderate", 2: "severe"} | |
| ANX_LABELS = {0: "none", 1: "mild", 2: "moderate", 3: "severe"} | |
| def analyze_audio(audio_filepath: str) -> str: | |
| """ | |
| Accepts an audio file path, runs DAM inference, returns JSON string | |
| with depression/anxiety scores (both quantized and raw). | |
| """ | |
| if audio_filepath is None: | |
| return json.dumps({"error": "No audio provided"}) | |
| if dam_pipeline is None: | |
| return json.dumps({"error": "Model not loaded"}) | |
| try: | |
| # Pre-process: convert to mono if needed | |
| waveform, sample_rate = torchaudio.load(audio_filepath) | |
| if waveform.shape[0] > 1: | |
| waveform = torch.mean(waveform, dim=0, keepdim=True) | |
| audio_filepath = "temp_mono.wav" | |
| torchaudio.save(audio_filepath, waveform, sample_rate) | |
| # Run inference (both quantized and raw) | |
| res_q = dam_pipeline.run_on_file(audio_filepath, quantize=True) | |
| res_r = dam_pipeline.run_on_file(audio_filepath, quantize=False) | |
| # Extract and normalize scores | |
| dep_q = int(res_q.get("depression", 0).item() if hasattr(res_q.get("depression", 0), "item") else res_q.get("depression", 0)) | |
| anx_q = int(res_q.get("anxiety", 0).item() if hasattr(res_q.get("anxiety", 0), "item") else res_q.get("anxiety", 0)) | |
| dep_r = float(res_r.get("depression", 0.0).item() if hasattr(res_r.get("depression", 0.0), "item") else res_r.get("depression", 0.0)) | |
| anx_r = float(res_r.get("anxiety", 0.0).item() if hasattr(res_r.get("anxiety", 0.0), "item") else res_r.get("anxiety", 0.0)) | |
| result = { | |
| "depression": dep_q, | |
| "depression_label": DEP_LABELS.get(dep_q, "unknown"), | |
| "anxiety": anx_q, | |
| "anxiety_label": ANX_LABELS.get(anx_q, "unknown"), | |
| "raw_depression": round(dep_r, 4), | |
| "raw_anxiety": round(anx_r, 4), | |
| "model": "KintsugiHealth/dam", | |
| } | |
| return json.dumps(result) | |
| except Exception as e: | |
| return json.dumps({"error": str(e)}) | |
| # Clean up temp file after processing | |
| def analyze_and_cleanup(audio_filepath: str) -> str: | |
| result = analyze_audio(audio_filepath) | |
| # Delete temp mono file if created | |
| if os.path.exists("temp_mono.wav"): | |
| try: | |
| os.unlink("temp_mono.wav") | |
| except OSError: | |
| pass | |
| return result | |
| demo = gr.Interface( | |
| fn=analyze_and_cleanup, | |
| inputs=gr.Audio(type="filepath", label="Upload audio or record (30+ seconds recommended)"), | |
| outputs=gr.Textbox(label="Analysis Result (JSON)", lines=10), | |
| title="Loop Mind — Voice Biomarker Analysis", | |
| description="Powered by KintsugiHealth/DAM. Returns depression and anxiety severity scores from voice acoustic features. For research and wellness tracking only — not a clinical diagnosis.", | |
| theme="soft", | |
| ) | |
| demo.launch() | |