File size: 5,668 Bytes
7e6c03a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
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()