File size: 7,636 Bytes
3416e1b 9a40160 3416e1b 9a40160 cc0b037 3416e1b 9a40160 3416e1b b2cb59a 3416e1b cc0b037 3416e1b 43132d8 3416e1b 9a40160 3416e1b 43132d8 3416e1b 9a40160 924bfa3 9a40160 924bfa3 9a40160 3416e1b 9a40160 924bfa3 3416e1b daf6e1e 3416e1b 9a40160 3416e1b 9a40160 3416e1b 9a40160 3416e1b daf6e1e 9a40160 3416e1b 9a40160 daf6e1e cc0b037 3416e1b 9a40160 3416e1b 9a40160 3416e1b 9a40160 3416e1b 9a40160 4769ba8 9a40160 3416e1b | 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 | # -*- coding: utf-8 -*-
"""
Cleaned fake vs forged vs real audio module (Gradio UI & Notebook Magics removed)
Fixed for PyTorch 2.6+ compatibility with LAION-CLAP checkpoints
"""
import os
import urllib.request
from huggingface_hub import login
import torch
import torch.serialization
import librosa
import numpy as np
import numpy
import laion_clap
import whisper
from sklearn.metrics.pairwise import cosine_similarity, euclidean_distances
from sklearn.preprocessing import normalize
from transformers import AutoFeatureExtractor, AutoModelForAudioClassification, AutoModelForAudioFrameClassification
from groq import Groq
# --- FETCH SECRETS & LOGIN ---
HF_TOKEN = os.environ.get("HF_TOKEN")
GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
if HF_TOKEN:
login(token=HF_TOKEN)
print("β
Login Successful! Ab aap models load kar sakti hain.")
else:
print("β οΈ WARNING: HF_TOKEN not found in Secrets. Private models load nahi honge.")
# --- SETUP & CONFIG ---
client = Groq(api_key=GROQ_API_KEY)
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
AST_MODEL_PATH = "aneela-pervez/FAKE-AUDIO"
# --- CATEGORIZED FORENSIC LABELS ---
FORENSIC_CATEGORIES = {
"AUTHENTIC": ["Natural human speech", "Human voice with natural breathing", "Consistent room ambiance"],
"AI_SYNTHETIC": ["AI voice clone robotic smoothness", "Synthetic neural vocoder", "Deepfake metallic resonance"],
"MANIPULATED": ["Manually spliced audio", "Inconsistent stitching artifacts", "Micro-cuts and clicks"],
"DISGUISED": ["Artificial pitch shifting", "Muffled voice mask identity", "Time-stretched artifacts"]
}
ALL_LABELS = [item for sublist in FORENSIC_CATEGORIES.values() for item in sublist]
# --- MODEL LOADING ---
print("π Initializing Fuzzy Forensic Engine v3.9...")
whisper_model = whisper.load_model("base", device=DEVICE)
id_extractor = AutoFeatureExtractor.from_pretrained("facebook/wav2vec2-base-960h")
id_model = AutoModelForAudioFrameClassification.from_pretrained("facebook/wav2vec2-base-960h").to(DEVICE).eval()
try:
ast_extractor = AutoFeatureExtractor.from_pretrained(AST_MODEL_PATH)
ast_model = AutoModelForAudioClassification.from_pretrained(AST_MODEL_PATH).to(DEVICE).eval()
except Exception as e:
print(f"β οΈ AST Model issue (using default logic if fails): {e}")
# --- CLAP MODEL LOADING (PyTorch 2.6+ Full Fix) ---
# Step 1: Checkpoint pehle local mein download karein
CLAP_CKPT_URL = "https://huggingface.co/lukewys/laion_clap/resolve/main/music_audioset_epoch_15_esc_90.14.pt"
CLAP_CKPT_PATH = "/tmp/clap_music_audioset.pt"
if not os.path.exists(CLAP_CKPT_PATH):
print("Downloading CLAP checkpoint to local disk...")
urllib.request.urlretrieve(CLAP_CKPT_URL, CLAP_CKPT_PATH)
print("β
CLAP checkpoint downloaded!")
else:
print("β
CLAP checkpoint already cached.")
# Step 2: PyTorch 2.6+ safe globals fix
torch.serialization.add_safe_globals([numpy.core.multiarray.scalar])
# Step 3: CLAP model initialize karein
clap_model = laion_clap.CLAP_Module(enable_fusion=False, amodel='HTSAT-tiny').to(DEVICE)
# Step 4: Manually load checkpoint with strict=False
# strict=False: unexpected/missing keys jaise position_ids ko ignore karta hai
print("π Loading CLAP checkpoint (strict=False)...")
ckpt = torch.load(CLAP_CKPT_PATH, map_location=DEVICE, weights_only=False)
# Checkpoint structure check
if "state_dict" in ckpt:
state_dict = ckpt["state_dict"]
elif "model" in ckpt:
state_dict = ckpt["model"]
else:
state_dict = ckpt
clap_model.model.load_state_dict(state_dict, strict=False)
clap_model.eval()
print("β
CLAP model loaded successfully!")
def extract_dna_embeddings(y):
y = np.ascontiguousarray(y)
inputs = id_extractor(y, sampling_rate=16000, return_tensors="pt")
inputs = {k: (v.clone().detach() if isinstance(v, torch.Tensor) else torch.tensor(v)).to(DEVICE) for k, v in inputs.items()}
with torch.no_grad():
logits = id_model(**inputs).logits
emb = torch.mean(logits, dim=1).cpu().numpy()
return normalize(emb)
def analyze_voice_forensics(audio_path):
try:
y, sr = librosa.load(audio_path, sr=16000)
y_48, _ = librosa.load(audio_path, sr=48000)
y = y / (np.max(np.abs(y)) + 1e-9)
y = np.ascontiguousarray(y)
# 1. SEGMENT-WISE CROSS VALIDATION
segments = np.array_split(y, 3)
seg_embs = [extract_dna_embeddings(s) for s in segments]
cos_sim = cosine_similarity(seg_embs[0], seg_embs[-1])[0][0]
euc_dist = euclidean_distances(seg_embs[0], seg_embs[-1])[0][0]
# 2. FEATURE EXTRACTION
transcription = whisper_model.transcribe(audio_path)["text"]
inputs = ast_extractor(y, sampling_rate=16000, return_tensors="pt")
inputs = {k: (v.clone().detach() if isinstance(v, torch.Tensor) else torch.tensor(v)).to(DEVICE) for k, v in inputs.items()}
with torch.no_grad():
ast_prob = torch.nn.functional.softmax(ast_model(**inputs).logits, dim=-1).cpu().numpy()[0][1]
ast_score = ast_prob * 100
# CLAP EMBEDDINGS
audio_emb_np = clap_model.get_audio_embedding_from_data(x=[y_48])
text_emb_np = clap_model.get_text_embedding(ALL_LABELS)
audio_emb = torch.from_numpy(audio_emb_np).to(DEVICE)
text_emb = torch.from_numpy(text_emb_np).to(DEVICE)
c_sims = torch.nn.functional.cosine_similarity(audio_emb, text_emb)
top_idx = torch.argmax(c_sims).item()
clap_label = ALL_LABELS[top_idx]
category = "UNKNOWN"
for cat, labels in FORENSIC_CATEGORIES.items():
if clap_label in labels:
category = cat
break
num_edits = len(np.where(np.diff(librosa.onset.onset_strength(y=y, sr=16000)) > 6.5)[0])
# FUZZY LOGIC MEMBERSHIP SCORING
mu_ai = min(1.0, max(0.0, (ast_score - 40) / 45))
mu_spliced = min(1.0, max(0.0, (num_edits - 8) / 10))
dna_conf = (cos_sim - 0.85) / 0.10
cat_conf = 1.0 if category == "AUTHENTIC" else 0.0
mu_auth = min(1.0, max(0.0, (dna_conf + cat_conf) / 2))
# FUZZY DECISION ENGINE (V4.0)
if mu_auth > 0.80 and mu_spliced < 0.75:
verdict = "β
AUTHENTIC HUMAN VOICE"
category_override = "AUTHENTIC"
elif mu_ai > 0.85 and mu_auth < 0.80:
verdict = "π¨ AI CLONE DETECTED"
category_override = "AI_SYNTHETIC"
else:
verdict = "β οΈ MANIPULATED REAL VOICE (Spliced)"
category_override = "MANIPULATED"
# LLM REASONING
audit_context = (f"Verdict: {verdict}. Category: {category_override}. Metrics: ID {cos_sim:.2f}, "
f"Texture {ast_score:.1f}%, Cuts {num_edits}. "
f"Fuzzy Scores: AI={mu_ai:.2f}, Spliced={mu_spliced:.2f}, Auth={mu_auth:.2f}")
prompt = (f"Act as a Senior Forensic Auditor. {audit_context} "
"Instruction: Justify the verdict. If identity stability is high (Auth > 0.8), "
"emphasize that DNA integrity confirms human origin despite high spectral texture. "
"Give a 2-line direct forensic conclusion.")
res = client.chat.completions.create(
model="openai/gpt-oss-120b",
messages=[{"role": "user", "content": prompt}]
)
reasoning = res.choices[0].message.content
evidence = f"Category: {category} | ID: {cos_sim:.2f} | Texture: {ast_score:.1f}% | Cuts: {num_edits}"
return verdict, evidence, reasoning
except Exception as e:
return "Error", str(e), "N/A" |