TREA_2.0_codebase / preprocess_urbansound8k.py
malay-36's picture
Upload updated pipeline codebase
7e6c03a verified
Raw
History Blame Contribute Delete
5.67 kB
from pathlib import Path
from concurrent.futures import ProcessPoolExecutor, as_completed
import os
import numpy as np
import pandas as pd
import soundfile as sf
from scipy.signal import resample_poly
from math import gcd
from tqdm import tqdm
# ============================================================
# Paths
# ============================================================
AUDIO_ROOT = Path("/home/debarpanb1/TREA_2.0/UrbanSound8K/audio")
CSV_PATH = Path("/home/debarpanb1/TREA_2.0/UrbanSound8K/metadata/UrbanSound8K.csv")
OUTPUT_ROOT = Path("/home/debarpanb1/TREA_2.0/UrbanSound8K_preprocessed_fast")
OUTPUT_AUDIO_ROOT = OUTPUT_ROOT / "audio"
OUTPUT_METADATA_PATH = OUTPUT_ROOT / "UrbanSound8K_with_audio_metadata.csv"
OUTPUT_AUDIO_ROOT.mkdir(parents=True, exist_ok=True)
# ============================================================
# Config
# ============================================================
TARGET_SR = 44100
TARGET_SUBTYPE = "PCM_16"
NORMALIZE_PEAK = True
# Use fewer workers if storage is slow.
NUM_WORKERS = min(16, os.cpu_count() or 4)
# ============================================================
# Helpers
# ============================================================
def safe_class_name(x):
return str(x).replace(" ", "_").replace("/", "_")
def make_output_filename(row):
fold = int(row["fold"])
class_name = safe_class_name(row["class"])
stem = Path(row["slice_file_name"]).stem
return f"fold{fold}_{class_name}_{stem}.wav"
def to_mono(audio):
if audio.ndim == 1:
return audio
return audio.mean(axis=1)
def resample_audio(audio, orig_sr, target_sr):
if orig_sr == target_sr:
return audio
factor = gcd(orig_sr, target_sr)
up = target_sr // factor
down = orig_sr // factor
return resample_poly(audio, up, down)
def peak_normalize(audio, eps=1e-8):
peak = np.max(np.abs(audio)) if len(audio) > 0 else 0.0
if peak < eps:
return audio
return audio / peak * 0.98
def process_one(row_dict):
fold = int(row_dict["fold"])
filename = row_dict["slice_file_name"]
original_audio_path = AUDIO_ROOT / f"fold{fold}" / filename
output_filename = make_output_filename(row_dict)
clean_audio_path = OUTPUT_AUDIO_ROOT / f"fold{fold}" / output_filename
clean_audio_path.parent.mkdir(parents=True, exist_ok=True)
row_dict["original_audio_path"] = str(original_audio_path)
row_dict["clean_audio_path"] = str(clean_audio_path)
row_dict["preprocess_target_sample_rate"] = TARGET_SR
row_dict["preprocess_target_channels"] = 1
row_dict["preprocess_target_subtype"] = TARGET_SUBTYPE
row_dict["preprocess_peak_normalize"] = NORMALIZE_PEAK
row_dict["preprocess_success"] = False
row_dict["preprocess_error"] = ""
if not original_audio_path.exists():
row_dict["preprocess_error"] = "missing_audio_file"
return row_dict
try:
# Original metadata
info = sf.info(str(original_audio_path))
row_dict["original_sample_rate"] = info.samplerate
row_dict["original_channels"] = info.channels
row_dict["original_frames"] = info.frames
row_dict["original_duration"] = info.frames / info.samplerate
row_dict["original_format"] = info.format
row_dict["original_subtype"] = info.subtype
row_dict["csv_slice_duration"] = float(row_dict["end"]) - float(row_dict["start"])
# Fast read
audio, sr = sf.read(str(original_audio_path), dtype="float32", always_2d=False)
# Mono
audio = to_mono(audio)
# Resample only if needed
audio = resample_audio(audio, sr, TARGET_SR)
# Normalize
if NORMALIZE_PEAK:
audio = peak_normalize(audio)
# Save
sf.write(str(clean_audio_path), audio, TARGET_SR, subtype=TARGET_SUBTYPE)
# Clean metadata
clean_info = sf.info(str(clean_audio_path))
row_dict["clean_sample_rate"] = clean_info.samplerate
row_dict["clean_channels"] = clean_info.channels
row_dict["clean_frames"] = clean_info.frames
row_dict["clean_duration"] = clean_info.frames / clean_info.samplerate
row_dict["clean_format"] = clean_info.format
row_dict["clean_subtype"] = clean_info.subtype
row_dict["preprocess_action"] = "soundfile_scipy_resample_to_44100_mono_pcm16_peaknorm"
row_dict["preprocess_success"] = True
except Exception as e:
row_dict["preprocess_error"] = repr(e)
return row_dict
def main():
df = pd.read_csv(CSV_PATH)
records = df.to_dict("records")
results = []
print(f"Processing {len(records)} files with {NUM_WORKERS} workers...")
with ProcessPoolExecutor(max_workers=NUM_WORKERS) as executor:
futures = [executor.submit(process_one, row) for row in records]
for future in tqdm(as_completed(futures), total=len(futures)):
results.append(future.result())
out_df = pd.DataFrame(results)
out_df.to_csv(OUTPUT_METADATA_PATH, index=False)
print("\nDone.")
print(f"Preprocessed audio saved to: {OUTPUT_AUDIO_ROOT}")
print(f"Metadata saved to: {OUTPUT_METADATA_PATH}")
print("\nSuccess count:")
print(out_df["preprocess_success"].value_counts(dropna=False))
print("\nOriginal sample rates:")
print(out_df["original_sample_rate"].value_counts(dropna=False).sort_index())
print("\nOriginal channels:")
print(out_df["original_channels"].value_counts(dropna=False).sort_index())
print("\nOriginal subtypes:")
print(out_df["original_subtype"].value_counts(dropna=False))
if __name__ == "__main__":
main()