File size: 3,863 Bytes
a06c4af
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
"""
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()