File size: 8,388 Bytes
87be748
8128e5b
87be748
 
 
 
8128e5b
 
87be748
4f3d2d9
87be748
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66b9466
87be748
 
 
 
 
 
 
 
 
 
 
66b9466
 
87be748
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# -*- coding: utf-8 -*-
import numpy as np
import librosa
import librosa.display
import matplotlib.pyplot as plt
import noisereduce as nr
import hashlib
import os
import gradio as gr

class AuthenticMusicShield:
    def __init__(self):
        print("?? Lancement du Bouclier v4.4.5 (Correction seuil AI Cover Music)...")
        self.ia = None

    def load_model(self):
        if self.ia is None:
            try:
                from transformers import pipeline
                self.ia = pipeline("audio-classification", model="MIT/ast-finetuned-audioset-10-10-0.4593")
            except Exception:
                self.ia = None

    def get_sha256(self, path):
        with open(path, "rb") as f:
            return hashlib.sha256(f.read()).hexdigest()

    def generer_spectrogramme(self, y, sr):
        plt.figure(figsize=(10, 4))
        S = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=128)
        S_dB = librosa.power_to_db(S, ref=np.max)
        librosa.display.specshow(S_dB, sr=sr, x_axis='time', y_axis='mel', cmap='magma')
        plt.title('Analyse de Texture & Intégrité Vocale')
        plt.tight_layout()
        plot_path = "spectrum.png"
        plt.savefig(plot_path)
        plt.close()
        return plot_path

    def analyser_expert(self, path):
        if path is None:
            return None, "En attente...", None
        try:
            self.load_model()
            y, sr = librosa.load(path, sr=44100)

            # --- DÉBRUITAGE ADAPTATIF ---
            # Première passe légère pour détecter la source
            y_light = nr.reduce_noise(y=y, sr=sr, prop_decrease=0.3)
            pitches_light, magnitudes_light = librosa.piptrack(y=y_light, sr=sr)
            centroid_light = np.mean(librosa.feature.spectral_centroid(y=y_light, sr=sr))

            # Si centroïde bas = probable broadcast/radio ? débruitage doux
            if centroid_light < 3500:
                prop = 0.4  # Préserve la signature broadcast
                raison_denoise = "Débruitage doux (source broadcast détectée)"
            else:
                prop = 0.7  # Débruitage standard
                raison_denoise = "Débruitage standard"

            y_denoised = nr.reduce_noise(y=y, sr=sr, prop_decrease=prop)

            # --- MÉTRIQUES ---
            pitches, magnitudes = librosa.piptrack(y=y_denoised, sr=sr)
            mask = magnitudes > np.median(magnitudes)
            jitter = np.std(pitches[mask]) / 1000 if np.any(mask) else 0
            flatness = np.mean(librosa.feature.spectral_flatness(y=y_denoised))

            # --- DÉTECTION VINTAGE ---
            # Analyse du centroïde spectral — les enregistrements vintage ont
            # un centroïde plus bas (fréquences hautes absentes ou atténuées)
            centroid = np.mean(librosa.feature.spectral_centroid(y=y_denoised, sr=sr))
            zcr = np.mean(librosa.feature.zero_crossing_rate(y_denoised))
            is_vintage = centroid < 3000 and zcr < 0.06

            # --- ANALYSE IA ---
            top_label, score_ia = "Inconnu", 0
            if self.ia:
                res_ia = self.ia(path)
                top_label, score_ia = res_ia[0]['label'], res_ia[0]['score'] * 100

            # ==========================================
            # LOGIQUE DE CONFIANCE v4.4.3
            # ==========================================
            confiance = 50
            raisons = []

            # 1. BONUS VINTAGE
            if is_vintage:
                confiance += 15
                raisons.append("Signature vintage détectée (enregistrement ancien)")

            # 2. TEST TEXTURE
            if flatness > 0.0012:
                confiance += 20
                raisons.append("Texture organique détectée")
            else:
                confiance -= 25
                raisons.append("Signal trop pur (Signature numérique)")
                # Si signal trop pur + score IA faible ? annule les bonus vintage/broadcast
                if score_ia < 70:
                    confiance -= 20
                    raisons.append("Bonus annulé — signal trop pur + source incertaine")

            # 3. FILTRE ANTI-AI COVER (corrigé pour les chansons anciennes)
            if "Music" in top_label or "Singing" in top_label:
                if jitter > 1.0 or jitter < 0.09:
                    if is_vintage:
                        confiance += 10
                        raisons.append("Jitter élevé validé — enregistrement vintage analogique")
                    elif flatness < 0.0012:
                        # Seuil relevé : signal pas assez organique + jitter anormal = AI Cover
                        confiance -= 60
                        raisons.append("Instabilité vocale typique AI Cover")
                    else:
                        confiance -= 20
                        raisons.append("Jitter suspect — analyse inconclusif")
                else:
                    confiance += 20
                    raisons.append("Harmoniques naturelles validées")

            # 4. CAS RADIO/VOIX
            else:
                # Détection broadcast FM — jitter élevé est normal sur radio
                is_broadcast = (jitter >= 0.85 and centroid_light < 3500) or prop == 0.4

                if 0.10 < jitter < 0.80:
                    confiance += 25
                    raisons.append("Vibration humaine naturelle")
                elif jitter >= 0.85:
                    if is_broadcast and flatness > 0.0010:
                        # Broadcast validé seulement si signal pas trop pur
                        confiance += 20
                        raisons.append("Jitter élevé normal — compression broadcast FM")
                    elif is_broadcast and flatness <= 0.0010:
                        # Signal trop pur même pour broadcast = suspect
                        confiance -= 30
                        raisons.append("Signal trop pur pour broadcast — AI Cover suspect")
                    elif flatness > 0.0008:
                        confiance += 15
                        raisons.append("Instabilité liée au flux radio (Validé)")
                    else:
                        confiance -= 60
                        raisons.append("Instabilité artificielle (Deepfake)")

            confiance = max(0, min(100, confiance))

            # Verdict
            if confiance >= 70:
                verdict = "? AUTHENTIQUE CERTIFIÉ"
            elif 40 <= confiance < 70:
                verdict = "?? ANALYSE INCONCLUSIVE"
            else:
                verdict = "?? AI COVER / DEEPFAKE DÉTECTÉ"

            sha = self.get_sha256(path)

            report = f"\n{'='*45}\n??? RAPPORT v4.4.5\n{'='*45}\n"
            report += f" ??? SOURCE IA  : {top_label} ({score_ia:.1f}%)\n"
            report += f" ?? CONFIANCE  : {confiance}%\n"
            report += f" ?? JITTER     : {jitter:.4f}\n"
            report += f" ?? VINTAGE    : {'Oui' if is_vintage else 'Non'}\n"
            report += f" ?? DÉBRUITAGE : {raison_denoise}\n"
            report += f" ?? SHA256     : {sha[:24]}...\n"
            report += f"{'-'*45}\n >>> VERDICT : {verdict}\n >>> NOTES : {', '.join(raisons)}\n{'='*45}\n"

            return self.generer_spectrogramme(y, sr), report, {
                "Score": f"{confiance}%",
                "Jitter": round(jitter, 4),
                "Vintage": is_vintage,
                "Centroid": round(float(centroid), 1)
            }

        except Exception as e:
            return None, f"Erreur : {e}", None


# --- Interface Gradio ---
shield = AuthenticMusicShield()
with gr.Blocks(theme=gr.themes.Soft()) as demo:
    gr.Markdown("# ??? Antigravity Shield v4.4.5 PRO")
    gr.Markdown("### Détecteur d'authenticité audio — AI Cover & Deepfake")
    with gr.Row():
        with gr.Column():
            audio_input = gr.Audio(type="filepath", label="Audio Input")
            run_btn = gr.Button("?? ANALYSER", variant="primary")
        with gr.Column():
            image_output = gr.Image(label="Spectrogramme")
            report_output = gr.Textbox(label="Rapport", lines=14)
            metrics_output = gr.Json(label="Métriques")
    run_btn.click(
        fn=shield.analyser_expert,
        inputs=audio_input,
        outputs=[image_output, report_output, metrics_output]
    )

demo.launch(ssr_mode=False)