Spaces:
Running on Zero
Running on Zero
File size: 4,617 Bytes
127b976 | 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 | from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
from typing import Optional, Dict, Any
import pandas as pd
import torch
import torch.nn.functional as F
import torchaudio
from torch.utils.data import Dataset
from .labels import ATTACK_TO_ID, normalize_attack_type, make_explanation_label
@dataclass
class AudioConfig:
sample_rate: int = 16000
duration_sec: float = 4.0
random_crop: bool = False
@property
def num_samples(self) -> int:
return int(self.sample_rate * self.duration_sec)
def _to_float(value: Any, default: float = -1.0) -> float:
if pd.isna(value):
return default
value = str(value).strip().lower()
if value == "full":
return default
try:
return float(value)
except ValueError:
return default
def load_audio_fixed(
file_path: str | Path,
sample_rate: int = 16000,
num_samples: Optional[int] = None,
random_crop: bool = False,
) -> torch.Tensor:
"""Load audio as mono waveform, resample, then pad/crop to num_samples.
Returns:
wav: Tensor with shape [num_samples] if num_samples is provided,
otherwise [num_audio_samples].
"""
path = Path(file_path)
if not path.exists():
raise FileNotFoundError(f"Audio file not found: {path}")
wav, sr = torchaudio.load(str(path))
# Convert channels x samples -> mono samples
if wav.ndim == 2:
wav = wav.mean(dim=0)
else:
wav = wav.flatten()
if sr != sample_rate:
wav = torchaudio.functional.resample(wav, sr, sample_rate)
wav = wav.float()
if num_samples is None:
return wav
if wav.numel() < num_samples:
return F.pad(wav, (0, num_samples - wav.numel()))
if wav.numel() == num_samples:
return wav
if random_crop:
max_start = wav.numel() - num_samples
start = torch.randint(0, max_start + 1, (1,)).item()
else:
start = 0
return wav[start : start + num_samples]
class AuralGuardDataset(Dataset):
"""Dataset for AuralGuard-AASIST++.
Required CSV columns:
file_path,binary_label,attack_type
Optional columns:
start_fake,end_fake,dataset,split
"""
def __init__(
self,
metadata_csv: str | Path,
audio_config: Optional[AudioConfig] = None,
root_dir: str | Path | None = None,
) -> None:
self.metadata_csv = Path(metadata_csv)
self.df = pd.read_csv(self.metadata_csv)
self.audio_config = audio_config or AudioConfig()
self.root_dir = Path(root_dir) if root_dir is not None else None
required = {"file_path", "binary_label", "attack_type"}
missing = required - set(self.df.columns)
if missing:
raise ValueError(f"Metadata CSV is missing required columns: {sorted(missing)}")
def __len__(self) -> int:
return len(self.df)
def _resolve_path(self, path_value: str) -> Path:
path = Path(path_value)
if path.is_absolute():
return path
if self.root_dir is not None:
return self.root_dir / path
return path
def __getitem__(self, idx: int) -> Dict[str, Any]:
row = self.df.iloc[idx]
file_path = self._resolve_path(row["file_path"])
wav = load_audio_fixed(
file_path=file_path,
sample_rate=self.audio_config.sample_rate,
num_samples=self.audio_config.num_samples,
random_crop=self.audio_config.random_crop,
)
binary_label = int(row["binary_label"])
attack_type = normalize_attack_type(row["attack_type"])
attack_label = ATTACK_TO_ID[attack_type]
explanation_label = make_explanation_label(binary_label, attack_type)
item: Dict[str, Any] = {
"wav": wav,
"binary_label": torch.tensor(binary_label, dtype=torch.long),
"attack_label": torch.tensor(attack_label, dtype=torch.long),
"explanation_label": torch.tensor(explanation_label, dtype=torch.long),
"file_path": str(file_path),
"attack_type": attack_type,
"dataset": str(row["dataset"]) if "dataset" in row else "unknown",
"start_fake": _to_float(row["start_fake"]) if "start_fake" in row else -1.0,
"end_fake": _to_float(row["end_fake"]) if "end_fake" in row else -1.0,
}
return item
|