Spaces:
Running on Zero
Running on Zero
Download src/preprocessing/standardize.py from SAJIDIUBAT/bangla-ser-api: direct link, hf CLI and curl.
- Browser
- Download file 3.97 kB
-
https://huggingface.co/spaces/SAJIDIUBAT/bangla-ser-api/resolve/main/src/preprocessing/standardize.py
- Command line
-
hf download hf://spaces/SAJIDIUBAT/bangla-ser-api/src/preprocessing/standardize.py
-
curl -L -o standardize.py https://huggingface.co/spaces/SAJIDIUBAT/bangla-ser-api/resolve/main/src/preprocessing/standardize.py
3.97 kB
| """Audio Standardization Pipeline for Bangla Speech Emotion Recognition. | |
| Converts all raw audio files non-destructively: | |
| - Downmixes 2-channel stereo to 1-channel mono ((L+R)/2) | |
| - Resamples varying sample rates (48kHz, 44.1kHz) to standard 16,000 Hz | |
| - Normalizes peak amplitude to -1.0 dBFS (0.891) | |
| - Caps extreme duration outliers (> 10.0 seconds) to 10.0 seconds maximum | |
| - Saves clean, standardized 16-bit PCM WAV files to dataset/processed/<emotion>/ | |
| - Preserves the original raw dataset in dataset/BASE/ completely intact. | |
| """ | |
| import os | |
| from pathlib import Path | |
| from concurrent.futures import ProcessPoolExecutor | |
| import numpy as np | |
| import soundfile as sf | |
| import librosa | |
| from tqdm import tqdm | |
| from src.utils.config import ( | |
| RAW_DATA_DIR, | |
| PROCESSED_DATA_DIR, | |
| TARGET_SAMPLE_RATE, | |
| MAX_DURATION_SECONDS, | |
| EMOTION_CLASSES | |
| ) | |
| def process_single_audio(args): | |
| raw_path, out_path = args | |
| try: | |
| if os.path.exists(out_path): | |
| return True, raw_path, None | |
| # Read raw audio | |
| audio, sr = sf.read(raw_path, dtype='float32') | |
| # 1. Downmix stereo to mono if needed | |
| if audio.ndim > 1: | |
| audio = np.mean(audio, axis=1) | |
| # 2. Resample to 16,000 Hz if needed | |
| if sr != TARGET_SAMPLE_RATE: | |
| audio = librosa.resample(audio, orig_sr=sr, target_sr=TARGET_SAMPLE_RATE, res_type="soxr_hq") | |
| # 3. Handle extreme durations (cap at MAX_DURATION_SECONDS = 10.0s) | |
| max_samples = int(MAX_DURATION_SECONDS * TARGET_SAMPLE_RATE) | |
| if len(audio) > max_samples: | |
| # Keep the first 10 seconds of speech | |
| audio = audio[:max_samples] | |
| # 4. Normalize peak amplitude to -1.0 dBFS (~0.891) | |
| peak = np.max(np.abs(audio)) | |
| if peak > 0: | |
| target_peak = 10 ** (-1.0 / 20.0) # ~0.89125 | |
| audio = audio * (target_peak / peak) | |
| # 5. Save standardized audio as 16-bit PCM WAV | |
| os.makedirs(os.path.dirname(out_path), exist_ok=True) | |
| sf.write(out_path, audio, TARGET_SAMPLE_RATE, subtype='PCM_16') | |
| return True, raw_path, None | |
| except Exception as e: | |
| return False, raw_path, str(e) | |
| def run_audio_standardization(max_workers: int = 4): | |
| print("=" * 70) | |
| print("STARTING AUDIO STANDARDIZATION PIPELINE") | |
| print(f"Target format: Mono, {TARGET_SAMPLE_RATE} Hz, 16-bit PCM WAV") | |
| print(f"Target duration cap: {MAX_DURATION_SECONDS} seconds") | |
| print(f"Source Directory: {RAW_DATA_DIR}") | |
| print(f"Destination Directory: {PROCESSED_DATA_DIR}") | |
| print("=" * 70) | |
| tasks = [] | |
| for emotion in EMOTION_CLASSES: | |
| raw_emotion_dir = RAW_DATA_DIR / emotion | |
| processed_emotion_dir = PROCESSED_DATA_DIR / emotion | |
| processed_emotion_dir.mkdir(parents=True, exist_ok=True) | |
| for root, _, files in os.walk(raw_emotion_dir): | |
| for f in files: | |
| if f.lower().endswith(".wav"): | |
| raw_path = os.path.join(root, f) | |
| out_path = os.path.join(processed_emotion_dir, f) | |
| tasks.append((raw_path, out_path)) | |
| print(f"Total files queued for standardization: {len(tasks)}") | |
| failed = [] | |
| success_count = 0 | |
| with ProcessPoolExecutor(max_workers=max_workers) as executor: | |
| results = list(tqdm(executor.map(process_single_audio, tasks), total=len(tasks), desc="Standardizing Audio")) | |
| for success, raw_path, error_msg in results: | |
| if success: | |
| success_count += 1 | |
| else: | |
| failed.append((raw_path, error_msg)) | |
| print(f"\nStandardization Complete: {success_count}/{len(tasks)} files processed.") | |
| if failed: | |
| print(f"WARNING: {len(failed)} files failed:") | |
| for fp, err in failed[:5]: | |
| print(f" - {fp}: {err}") | |
| else: | |
| print("All 5,000 files standardized with ZERO errors.") | |
| print("=" * 70) | |
| if __name__ == "__main__": | |
| run_audio_standardization() | |