File size: 1,798 Bytes
191d479
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import gradio as gr
import json
import sys

sys.path.insert(0, ".")

# Models are loaded at startup, not per-request.
# Cold start time on CPU Basic: 3–8 min. This cost is paid once per Space
# wake-up, not on every extraction call.
from feature_extractor_cpu import extract_mir_features, ENG_DIMS
from clap_embedder import get_clap_embedding

print("[startup] All models loaded. Space ready.")


def extract(audio_file_path: str) -> str:
    """
    Receives a local file path from Gradio (gr.File type='filepath').
    Returns JSON with keys: l3, clap, eng_raw, true_peak_dbfs.
    Any exception propagates to gradio_client as a job error — no silent failures.
    """
    if audio_file_path is None:
        raise ValueError("No audio file received.")

    l3, eng_raw, true_peak = extract_mir_features(audio_file_path)
    clap = get_clap_embedding(audio_file_path)

    if len(eng_raw) != len(ENG_DIMS):
        raise ValueError(
            f"eng_raw length mismatch: got {len(eng_raw)}, expected {len(ENG_DIMS)}"
        )

    return json.dumps({
        "l3":             l3.tolist(),
        "clap":           clap.tolist(),
        "eng_raw":        eng_raw.tolist(),
        "true_peak_dbfs": float(true_peak),
    })


# demo.queue() is REQUIRED — not optional.
# Without it, Gradio's async event loop kills any function that runs longer
# than a few seconds. CPU demucs with shifts=5 runs for 33–136 min per track.
# queue() enables proper long-running background job execution.
demo = gr.Interface(
    fn=extract,
    inputs=gr.File(label="Audio file (.mp3 or .wav)", type="filepath"),
    outputs=gr.Textbox(label="Feature JSON"),
    title="Playlisting Feature Extractor (CPU Basic)",
)
demo.queue(max_size=50)

if __name__ == "__main__":
    demo.launch(show_error=True)