Spaces:
Runtime error
Runtime error
| import json | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| import torchaudio | |
| import soundfile as sf | |
| TARGET_SR = 44100 | |
| TARGET_DURATION = 5 | |
| TARGET_NUM_SAMPLES = TARGET_SR * TARGET_DURATION | |
| class NormalizeMeanStd(nn.Module): | |
| def __init__(self, mean: float, std: float, eps: float = 1e-6): | |
| super().__init__() | |
| self.register_buffer("mean", torch.tensor(mean).view(1, 1, 1)) | |
| self.register_buffer("std", torch.tensor(std).view(1, 1, 1)) | |
| self.eps = eps | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| return (x - self.mean) / (self.std + self.eps) | |
| def mel_transform_from_stats( | |
| stats_path: str, | |
| sample_rate: int = 44100, | |
| n_fft: int = 1024, | |
| hop_length: int = 256, | |
| n_mels: int = 40, | |
| ): | |
| with open(stats_path, "r", encoding="utf-8") as f: | |
| s = json.load(f) | |
| return nn.Sequential( | |
| torchaudio.transforms.MelSpectrogram( | |
| sample_rate=sample_rate, | |
| n_fft=n_fft, | |
| hop_length=hop_length, | |
| n_mels=n_mels, | |
| ), | |
| torchaudio.transforms.AmplitudeToDB(), | |
| NormalizeMeanStd(s["mean"], s["std"]), | |
| ) | |
| def load_audio_with_soundfile(file_path: str): | |
| waveform, sr = sf.read(file_path, always_2d=True) # [samples, channels] | |
| waveform = torch.tensor(waveform, dtype=torch.float32).transpose(0, 1) # [channels, samples] | |
| return waveform, sr | |
| def convert_to_mono(waveform: torch.Tensor) -> torch.Tensor: | |
| if waveform.shape[0] > 1: | |
| waveform = waveform.mean(dim=0, keepdim=True) | |
| return waveform | |
| def resample_if_needed(waveform: torch.Tensor, orig_sr: int, target_sr: int = TARGET_SR) -> torch.Tensor: | |
| if orig_sr != target_sr: | |
| resampler = torchaudio.transforms.Resample(orig_freq=orig_sr, new_freq=target_sr) | |
| waveform = resampler(waveform) | |
| return waveform | |
| def pad_or_trim(waveform: torch.Tensor, target_num_samples: int = TARGET_NUM_SAMPLES) -> torch.Tensor: | |
| num_samples = waveform.shape[1] | |
| if num_samples > target_num_samples: | |
| waveform = waveform[:, :target_num_samples] | |
| elif num_samples < target_num_samples: | |
| pad_amount = target_num_samples - num_samples | |
| waveform = F.pad(waveform, (0, pad_amount)) | |
| return waveform | |
| def preprocess_audio(file_path: str, stats_path: str) -> torch.Tensor: | |
| waveform, sr = load_audio_with_soundfile(file_path) | |
| waveform = convert_to_mono(waveform) | |
| waveform = resample_if_needed(waveform, sr, TARGET_SR) | |
| waveform = pad_or_trim(waveform, TARGET_NUM_SAMPLES) | |
| mel_transform = mel_transform_from_stats(stats_path=stats_path) | |
| features = mel_transform(waveform) # [1, 40, time] | |
| features = features.unsqueeze(0) # [1, 1, 40, time] | |
| return features |