Spaces:
Sleeping
Sleeping
File size: 4,331 Bytes
cdfb7d6 4dc7304 cdfb7d6 4dc7304 cdfb7d6 4dc7304 cdfb7d6 4dc7304 cdfb7d6 4dc7304 cdfb7d6 4dc7304 cdfb7d6 4dc7304 cdfb7d6 4dc7304 cdfb7d6 | 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 | import os
import pandas as pd
import librosa
import requests
import io
import soundfile as sf
import numpy as np
from tqdm import tqdm
class AudioPreprocessor:
def __init__(self, target_sr=16000, chunk_duration=3, stride_duration=1):
self.target_sr = target_sr
self.chunk_duration = chunk_duration
self.stride_duration = stride_duration
self.samples_per_chunk = int(target_sr * chunk_duration)
self.samples_per_stride = int(target_sr * stride_duration)
def download_audio(self, url):
"""Downloads audio from URL into memory."""
try:
response = requests.get(url, timeout=15)
response.raise_for_status()
return io.BytesIO(response.content)
except Exception as e:
print(f"Failed to download {url}: {e}")
return None
def process_and_split(self, audio_bytes, trim_silence=True, peak_norm=True):
"""Converts to mono, resamples, optionally trims silence, and splits using a rolling window."""
try:
audio_bytes.seek(0)
y, sr = librosa.load(audio_bytes, sr=self.target_sr, mono=True)
if len(y) == 0:
print("Warning: Downloaded audio is empty.")
return []
# Trim leading/trailing silence (gentle threshold to avoid cutting speech)
if trim_silence and len(y) > self.samples_per_chunk:
y_trim, _ = librosa.effects.trim(
y, top_db=25, frame_length=2048, hop_length=512
)
if len(y_trim) >= self.samples_per_chunk:
y = y_trim
chunks = []
# OVERLAPPING WINDOW: Step forward by samples_per_stride (1 second) instead of full chunk
for i in range(0, len(y), self.samples_per_stride):
chunk = y[i : i + self.samples_per_chunk].copy()
# Keep chunks that are at least 2.5 seconds long.
# (Shorter trailing pieces are safely dropped because the overlap caught them earlier)
min_required_samples = int(self.target_sr * 2.5)
if len(chunk) < min_required_samples:
continue
# Peak-normalize chunk so level is consistent across chunks
if peak_norm:
peak = np.abs(chunk).max()
if peak > 1e-8:
chunk = chunk / peak
chunks.append(chunk)
return chunks
except Exception as e:
print(f"Error processing audio: {e}")
return []
def run_pipeline(self, csv_path, output_dir):
"""Main execution loop."""
df = pd.read_csv(csv_path)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
processed_data = []
print(f"Starting processing of {len(df)} files...")
for _, row in tqdm(df.iterrows(), total=len(df)):
audio_io = self.download_audio(row["audio_url"])
if audio_io:
chunks = self.process_and_split(audio_io)
for idx, chunk in enumerate(chunks):
chunk_filename = f"{row['dp_id']}_chunk{idx}.wav"
chunk_path = os.path.join(output_dir, chunk_filename)
# Save chunk as .wav for the training pipeline
sf.write(chunk_path, chunk, self.target_sr)
processed_data.append(
{
"chunk_path": chunk_path,
"original_id": row["dp_id"],
"label": row["nativity_status"],
"language": row["language"],
}
)
# Save a new manifest for the ML model
manifest_df = pd.DataFrame(processed_data)
manifest_df.to_csv("manifests/processed_manifest.csv", index=False)
print(f"Done! Saved {len(manifest_df)} chunks to {output_dir}")
if __name__ == "__main__":
# Usage
preprocessor = AudioPreprocessor(chunk_duration=3, stride_duration=1)
# Ensure your csv has columns: 'audio_url', 'dp_id', 'nativity_status', 'language'
preprocessor.run_pipeline("manifests/input_data.csv", "data/processed_audio_chunks")
|