ADMLab commited on
Commit
1f7190b
·
verified ·
1 Parent(s): 6b14df4

Upload 3 files

Browse files
Files changed (2) hide show
  1. README.md +7 -14
  2. app.py +82 -86
README.md CHANGED
@@ -1,26 +1,24 @@
1
  ---
2
- title: ADMC AI Music Detection
3
  emoji: 🎵
4
  colorFrom: red
5
  colorTo: indigo
6
  sdk: gradio
7
- sdk_version: 6.9.0
8
  app_file: app.py
9
  pinned: true
10
  license: apache-2.0
11
  ---
12
 
13
- # 🎵 ADMC Rilevamento Autorialità AI nella Musica
14
 
15
  Parte del sistema di certificazione **ADMC (Artigiani della Musica Code)**.
16
 
17
- Analizza brani musicali per rilevare la presenza di generazione AI, supportando
18
- il rilascio del **Certificato di Paternità Umana dell'Opera Musicale**.
19
 
20
  ## API REST
21
 
22
  ### POST /analyze
23
- Invia audio grezzo e riceve lo score AI:
24
 
25
  ```bash
26
  curl -X POST https://YOUR-SPACE.hf.space/analyze \
@@ -37,16 +35,11 @@ Risposta:
37
  ```
38
 
39
  ### GET /health
 
40
  Verifica stato del servizio.
41
 
42
  ## Configurazione nel Plugin WordPress
43
 
44
  1. Copia l'URL del tuo Space (es. `https://artigianidellamusica-admc.hf.space`)
45
- 2. Incollalo nel campo **Endpoint personalizzato** nelle impostazioni del plugin ADMC
46
- 3. Aggiungi il tuo **Hugging Face API Token** per autenticarti
47
-
48
- ## Modello
49
-
50
- Il modello predefinito è `motheecreator/ai-music-detection`.
51
- È consigliato addestrare un modello custom su dataset di musica umana vs AI
52
- per massimizzare la precisione nel contesto specifico di ADMC.
 
1
  ---
2
+ title: ADMC AI Music Detection
3
  emoji: 🎵
4
  colorFrom: red
5
  colorTo: indigo
6
  sdk: gradio
7
+ sdk_version: "4.0"
8
  app_file: app.py
9
  pinned: true
10
  license: apache-2.0
11
  ---
12
 
13
+ # ADMC - Rilevamento Autorialita AI nella Musica
14
 
15
  Parte del sistema di certificazione **ADMC (Artigiani della Musica Code)**.
16
 
17
+ Modello usato: `AI-Music-Detection/ai_music_detection_large_60s`
 
18
 
19
  ## API REST
20
 
21
  ### POST /analyze
 
22
 
23
  ```bash
24
  curl -X POST https://YOUR-SPACE.hf.space/analyze \
 
35
  ```
36
 
37
  ### GET /health
38
+
39
  Verifica stato del servizio.
40
 
41
  ## Configurazione nel Plugin WordPress
42
 
43
  1. Copia l'URL del tuo Space (es. `https://artigianidellamusica-admc.hf.space`)
44
+ 2. Incollalo in **ADMC -> Impostazioni -> Endpoint personalizzato**: `https://YOUR-SPACE.hf.space/analyze`
45
+ 3. Aggiungi il tuo **Hugging Face API Token**
 
 
 
 
 
 
app.py CHANGED
@@ -1,130 +1,121 @@
1
  """
2
- ADMC AI Music Detection API
3
  Hugging Face Space (Gradio + FastAPI)
 
4
 
5
- Questo Space espone un endpoint REST che il plugin WordPress ADMC può chiamare
6
- per analizzare se un brano musicale è stato generato da AI o creato da un umano.
7
-
8
- Deploy su Hugging Face Spaces:
9
- 1. Crea un nuovo Space (tipo: Gradio, SDK: Python)
10
- 2. Carica questo file come app.py
11
- 3. Aggiungi requirements.txt (vedi sotto)
12
- 4. Configura l'URL del Space nelle impostazioni del plugin WordPress
13
-
14
- Il plugin WP invia il file audio grezzo in POST e riceve una risposta JSON
15
- nel formato standard HF audio-classification:
16
- [{"label": "AI", "score": 0.87}, {"label": "Human", "score": 0.13}]
17
  """
18
 
19
  import gradio as gr
20
  import numpy as np
21
- import librosa
22
  import torch
23
  from fastapi import FastAPI, Request, HTTPException
24
  from fastapi.responses import JSONResponse
25
  from transformers import pipeline
26
- import tempfile, os, io
 
27
 
28
- # ── Model loading ──────────────────────────────────────────────────────────
 
 
 
29
 
30
- MODEL_ID = "motheecreator/ai-music-detection" # Change to your fine-tuned model
31
-
32
- print(f"Loading model: {MODEL_ID}")
33
  try:
34
  classifier = pipeline(
35
  "audio-classification",
36
  model=MODEL_ID,
37
  device=0 if torch.cuda.is_available() else -1,
38
  )
39
- print("Model loaded successfully")
40
  except Exception as e:
41
- print(f"Warning: Could not load primary model ({e}). Using fallback feature extractor.")
42
- classifier = None
43
-
44
 
45
- def extract_features(audio_path: str) -> np.ndarray:
46
- """Extract mel-spectrogram features from audio file (30s excerpt)."""
47
- y, sr = librosa.load(audio_path, sr=22050, duration=30.0, mono=True)
48
- mel = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=128, fmax=8000)
49
- mel_db = librosa.power_to_db(mel, ref=np.max)
50
- return mel_db
51
 
 
 
52
 
53
- def analyze_audio(audio_path: str) -> list[dict]:
54
- """
55
- Run AI detection on an audio file.
56
- Returns list of {label, score} dicts.
57
- """
58
  if classifier is not None:
59
- # Use HF pipeline directly
60
- result = classifier(audio_path, top_k=2)
61
- # Normalise labels to 'AI' / 'Human'
62
- normalised = []
63
- for item in result:
64
- lbl = item["label"].upper()
65
- if any(k in lbl for k in ["AI", "GENERATED", "FAKE", "SYNTH"]):
66
- normalised.append({"label": "AI", "score": float(item["score"])})
67
- else:
68
- normalised.append({"label": "Human", "score": float(item["score"])})
69
- return normalised
70
-
71
- # ── Fallback: simple spectral analysis heuristic ──────────────────────
72
- # This is a placeholder. Replace with a trained model for production use.
73
- features = extract_features(audio_path)
74
-
75
- # Heuristic: AI music often has very uniform spectral distribution
76
- spectral_std = float(np.std(features))
77
- spectral_mean = float(np.mean(np.abs(features)))
78
-
79
- # Lower std relative to mean → more "uniform" → higher AI probability
80
- # This is a very rough heuristic – fine-tune a real model for production!
81
- ratio = spectral_std / (spectral_mean + 1e-6)
82
- ai_score = max(0.0, min(1.0, 1.0 - (ratio / 3.0)))
83
-
 
 
 
 
 
 
 
 
84
  return [
85
  {"label": "AI", "score": round(ai_score, 4)},
86
  {"label": "Human", "score": round(1.0 - ai_score, 4)},
87
  ]
88
 
89
 
90
- # ── Gradio UI (for human testing) ─────────────────────────────────────────
91
-
92
  def gradio_analyze(audio_file):
93
  if audio_file is None:
94
  return "Nessun file caricato."
95
- result = analyze_audio(audio_file)
96
- ai_score = next((r["score"] for r in result if r["label"] == "AI"), 0.0)
97
- human_score = next((r["score"] for r in result if r["label"] == "Human"), 1.0)
98
- verdict = "🤖 Probabile AI" if ai_score > 0.5 else "✅ Probabile umano"
99
  return (
100
- f"{verdict}\n\n"
101
- f"AI Score: {ai_score*100:.1f}%\n"
102
- f"Human Score: {human_score*100:.1f}%\n\n"
103
- f"Soglia ADMC: 50% (configurabile nel plugin WP)"
104
  )
105
 
 
 
106
  demo = gr.Interface(
107
  fn=gradio_analyze,
108
  inputs=gr.Audio(type="filepath", label="Carica brano musicale (MP3/WAV/FLAC)"),
109
  outputs=gr.Textbox(label="Risultato analisi ADMC"),
110
- title="🎵 ADMC Rilevamento Autorialità AI",
111
  description=(
112
- "Analizza un brano musicale per rilevare se è stato generato da sistemi AI generativi. "
113
- "Parte del sistema di certificazione ADMC Artigiani della Musica."
114
  ),
115
- examples=[],
116
- allow_flagging="never",
117
  )
118
 
119
- # ── FastAPI endpoint (called by WordPress plugin) ─────────────────────────
120
-
121
  app = gr.mount_gradio_app(FastAPI(), demo, path="/")
122
 
 
123
  @app.post("/analyze")
124
  async def api_analyze(request: Request):
125
  """
126
  POST /analyze
127
- Body: raw audio bytes (Content-Type: audio/mpeg | audio/wav | audio/flac ...)
 
128
  Returns: [{"label": "AI", "score": 0.87}, {"label": "Human", "score": 0.13}]
129
  """
130
  content_type = request.headers.get("content-type", "")
@@ -136,29 +127,34 @@ async def api_analyze(request: Request):
136
  "audio/ogg": ".ogg",
137
  "audio/aiff": ".aiff",
138
  }
139
- ext = ext_map.get(content_type.split(";")[0].strip(), ".mp3")
 
140
 
141
- audio_bytes = await request.body()
142
- if len(audio_bytes) == 0:
143
  raise HTTPException(status_code=400, detail="Nessun file audio ricevuto.")
144
- if len(audio_bytes) > 100 * 1024 * 1024:
145
  raise HTTPException(status_code=413, detail="File troppo grande (max 100 MB).")
146
 
147
- # Save to temp file
148
  with tempfile.NamedTemporaryFile(suffix=ext, delete=False) as tmp:
149
- tmp.write(audio_bytes)
150
  tmp_path = tmp.name
151
 
152
  try:
153
  result = analyze_audio(tmp_path)
154
  except Exception as e:
155
- raise HTTPException(status_code=500, detail=f"Errore analisi: {str(e)}")
156
  finally:
157
- os.unlink(tmp_path)
 
158
 
159
  return JSONResponse(content=result)
160
 
161
 
162
  @app.get("/health")
163
  async def health():
164
- return {"status": "ok", "model": MODEL_ID, "gpu": torch.cuda.is_available()}
 
 
 
 
 
 
1
  """
2
+ ADMC - AI Music Detection API
3
  Hugging Face Space (Gradio + FastAPI)
4
+ v2 - Fix: modello corretto + compatibilita Gradio 5+
5
 
6
+ Correzioni rispetto a v1:
7
+ 1. Modello corretto: AI-Music-Detection/ai_music_detection_large_60s
8
+ (il precedente motheecreator/ai-music-detection non esiste)
9
+ 2. allow_flagging rimosso (deprecato in Gradio 4+, rimpiazzato da flagging_mode)
 
 
 
 
 
 
 
 
10
  """
11
 
12
  import gradio as gr
13
  import numpy as np
 
14
  import torch
15
  from fastapi import FastAPI, Request, HTTPException
16
  from fastapi.responses import JSONResponse
17
  from transformers import pipeline
18
+ import tempfile
19
+ import os
20
 
21
+ # Modello specifico per rilevamento musica AI vs umana
22
+ # Addestrato su SleepyJesse/ai_music_large
23
+ # LABEL_0 = Human, LABEL_1 = AI generated
24
+ MODEL_ID = "AI-Music-Detection/ai_music_detection_large_60s"
25
 
26
+ print("Loading model: " + MODEL_ID)
27
+ classifier = None
 
28
  try:
29
  classifier = pipeline(
30
  "audio-classification",
31
  model=MODEL_ID,
32
  device=0 if torch.cuda.is_available() else -1,
33
  )
34
+ print("Model loaded successfully")
35
  except Exception as e:
36
+ print("Warning: Could not load model (" + str(e) + "). Using fallback heuristic.")
 
 
37
 
 
 
 
 
 
 
38
 
39
+ def analyze_audio(audio_path):
40
+ """Analizza file audio, restituisce lista [{label, score}]."""
41
 
 
 
 
 
 
42
  if classifier is not None:
43
+ try:
44
+ result = classifier(audio_path, top_k=2)
45
+ ai_score = 0.5
46
+ for item in result:
47
+ lbl = item["label"].upper()
48
+ # AI-Music-Detection usa LABEL_0/LABEL_1
49
+ # LABEL_1 = AI, LABEL_0 = Human (verificato dal model card)
50
+ if "LABEL_1" in lbl or "AI" in lbl or "FAKE" in lbl:
51
+ ai_score = float(item["score"])
52
+ break
53
+ if "LABEL_0" in lbl or "HUMAN" in lbl or "REAL" in lbl:
54
+ ai_score = 1.0 - float(item["score"])
55
+ break
56
+ return [
57
+ {"label": "AI", "score": round(ai_score, 4)},
58
+ {"label": "Human", "score": round(1.0 - ai_score, 4)},
59
+ ]
60
+ except Exception as e:
61
+ print("Inference error: " + str(e))
62
+
63
+ # Fallback euristico con librosa se disponibile
64
+ try:
65
+ import librosa
66
+ y, sr = librosa.load(audio_path, sr=22050, duration=30.0, mono=True)
67
+ mel = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=128, fmax=8000)
68
+ mel_db = librosa.power_to_db(mel, ref=np.max)
69
+ std = float(np.std(mel_db))
70
+ mean = float(np.mean(np.abs(mel_db)))
71
+ ratio = std / (mean + 1e-6)
72
+ ai_score = max(0.0, min(1.0, 1.0 - (ratio / 3.0)))
73
+ except Exception:
74
+ ai_score = 0.5
75
+
76
  return [
77
  {"label": "AI", "score": round(ai_score, 4)},
78
  {"label": "Human", "score": round(1.0 - ai_score, 4)},
79
  ]
80
 
81
 
 
 
82
  def gradio_analyze(audio_file):
83
  if audio_file is None:
84
  return "Nessun file caricato."
85
+ result = analyze_audio(audio_file)
86
+ ai_score = next((r["score"] for r in result if r["label"] == "AI"), 0.5)
87
+ verdict = "Probabile AI" if ai_score > 0.5 else "Probabile umano (autorialita' umana)"
 
88
  return (
89
+ verdict + "\n\n"
90
+ "AI Score: " + str(round(ai_score * 100, 1)) + "%\n"
91
+ "Human Score: " + str(round((1 - ai_score) * 100, 1)) + "%\n\n"
92
+ "Soglia ADMC: 50% (configurabile nel plugin WordPress)"
93
  )
94
 
95
+
96
+ # Gradio 4+ usa flagging_mode="never" invece di allow_flagging="never"
97
  demo = gr.Interface(
98
  fn=gradio_analyze,
99
  inputs=gr.Audio(type="filepath", label="Carica brano musicale (MP3/WAV/FLAC)"),
100
  outputs=gr.Textbox(label="Risultato analisi ADMC"),
101
+ title="ADMC - Rilevamento Autorialita AI nella Musica",
102
  description=(
103
+ "Analizza un brano musicale per rilevare se e stato generato da AI o creato da un umano. "
104
+ "Parte del sistema di certificazione ADMC - Artigiani della Musica."
105
  ),
106
+ flagging_mode="never",
 
107
  )
108
 
109
+ # Monta Gradio su FastAPI
 
110
  app = gr.mount_gradio_app(FastAPI(), demo, path="/")
111
 
112
+
113
  @app.post("/analyze")
114
  async def api_analyze(request: Request):
115
  """
116
  POST /analyze
117
+ Body: raw audio bytes
118
+ Content-Type: audio/mpeg | audio/wav | audio/flac | audio/ogg
119
  Returns: [{"label": "AI", "score": 0.87}, {"label": "Human", "score": 0.13}]
120
  """
121
  content_type = request.headers.get("content-type", "")
 
127
  "audio/ogg": ".ogg",
128
  "audio/aiff": ".aiff",
129
  }
130
+ ext = ext_map.get(content_type.split(";")[0].strip(), ".mp3")
131
+ audio_data = await request.body()
132
 
133
+ if len(audio_data) == 0:
 
134
  raise HTTPException(status_code=400, detail="Nessun file audio ricevuto.")
135
+ if len(audio_data) > 100 * 1024 * 1024:
136
  raise HTTPException(status_code=413, detail="File troppo grande (max 100 MB).")
137
 
 
138
  with tempfile.NamedTemporaryFile(suffix=ext, delete=False) as tmp:
139
+ tmp.write(audio_data)
140
  tmp_path = tmp.name
141
 
142
  try:
143
  result = analyze_audio(tmp_path)
144
  except Exception as e:
145
+ raise HTTPException(status_code=500, detail="Errore analisi: " + str(e))
146
  finally:
147
+ if os.path.exists(tmp_path):
148
+ os.unlink(tmp_path)
149
 
150
  return JSONResponse(content=result)
151
 
152
 
153
  @app.get("/health")
154
  async def health():
155
+ return {
156
+ "status": "ok",
157
+ "model": MODEL_ID,
158
+ "loaded": classifier is not None,
159
+ "gpu": torch.cuda.is_available(),
160
+ }