| import soundfile as sf |
| import torchaudio |
|
|
| def read_wav_segment(file_path, start=None, end=None, dtype="float32"): |
| """ |
| Reads a specific segment from a .wav file efficiently. |
| |
| Args: |
| file_path (str): Path to the .wav file. |
| start (int): Start frame index. |
| end (int): End frame index. |
| |
| Returns: |
| numpy.ndarray: Audio data for the specified segment. |
| int: Sample rate of the audio file. |
| """ |
| |
| if start is None or end is None: |
| data, samplerate = sf.read(file_path, dtype=dtype) |
| else: |
| with sf.SoundFile(file_path) as audio_file: |
| |
| audio_file.seek(start) |
| data = audio_file.read(frames=end-start, dtype=dtype) |
| samplerate = audio_file.samplerate |
|
|
| return data, samplerate |
|
|
| def get_audio_length(file_path): |
| """ |
| Retrieves the length of an audio file in seconds and frames. |
| |
| Args: |
| file_path (str): Path to the audio file. |
| |
| Returns: |
| float: Length of the audio file in seconds. |
| int: Total number of frames in the audio file. |
| int: Sample rate of the audio file. |
| """ |
| with sf.SoundFile(file_path) as audio_file: |
| total_frames = len(audio_file) |
| samplerate = audio_file.samplerate |
| duration = total_frames / samplerate |
|
|
| return duration, total_frames, samplerate |
|
|
| def taxonomy2track(input_class, num_instr=8): |
|
|
| assert num_instr==8, "num_instr should be 8 for this function, the rest is not implemented yet" |
|
|
| if input_class is None: |
| return 'unknown' |
| if num_instr == 8: |
| mapping = {0000: 'other', 1100: 'drums', 1200: 'drums', 1300: 'other', 2000: 'bass', 3000: 'guitar', 4100: 'piano', 4200: 'piano', 4300: 'piano', 4400: 'other', 4500: 'other', 4600: 'other', 4700: 'other', 4900: 'other', 5000: 'brass', 6100: 'strings', 6210: 'brass', 6220: 'brass', 8100: 'guitar', 8200: 'brass', 9000: 'vocals'} |
| else: |
| raise NotImplementedError() |
| |
| code_length = len(str(input_class)) |
| if code_length < 4: |
| |
| input_class = int(str(input_class) + "0" * (4 - code_length)) |
| |
| class_str = str(input_class) |
| for i in range(len(class_str), 0, -1): |
| general_class = int(class_str[:i] + "0" * (len(class_str) - i)) |
| if general_class in mapping: |
| return mapping[general_class] |
| |
| try: |
| raise ValueError(f"No mapping found for input class {input_class} with num_instr {num_instr}") |
| except ValueError as e: |
| print(f"Error: {e}") |
| return "other" |
|
|
| import torch |
| def efficient_roll(x, shift, dims=-1): |
| """ |
| Efficiently roll tensor elements along a dimension without creating a full copy. |
| |
| Args: |
| x: Input tensor |
| shift: Number of places to roll (negative for left roll) |
| dim: Dimension along which to roll |
| |
| Returns: |
| Rolled tensor view where possible, minimal copy where necessary |
| """ |
| if shift == 0: |
| return x |
| |
| |
| dim_size = x.size(dims) |
| |
| |
| shift = shift % dim_size |
| if shift < 0: |
| shift += dim_size |
| |
| |
| indices = torch.cat([torch.arange(dim_size-shift, dim_size), |
| torch.arange(0, dim_size-shift)]) |
| |
| |
| return torch.index_select(x, dims, indices) |
|
|
| |
| import numpy as np |
|
|
| def apply_loud_normalization(x, lufs=-23, sample_rate=44100,device=None): |
| """ |
| x shaPe: (batch_size, channels, time) |
| """ |
|
|
| in_shape= x.shape |
| if x.ndim != 3: |
| x=x.view(-1, in_shape[-2], in_shape[-1]) |
|
|
| B, C, T = x.shape |
|
|
| if device is None: |
| device = x.device |
|
|
| x_out = torch.zeros_like(x) |
| |
| |
| |
|
|
| |
| |
|
|
| |
|
|
| x=x.view(B* C,1, T) |
|
|
| loudness=torchaudio.functional.loudness(x+1e-5, sample_rate=sample_rate) |
| delta_loudness = lufs - loudness |
| gain= torch.pow(10, delta_loudness / 20) |
| if gain.isnan().any(): |
| print("NaN detected in gain, setting to -30 dB") |
| gain = torch.nan_to_num(gain, nan=-30.0) |
|
|
| x_out = x * gain.view(B * C, 1, 1) |
|
|
| |
| x_out = x_out.view(in_shape) |
|
|
| return x_out |
|
|
|
|
|
|
| from utils.feature_extractors.dsp_features import compute_log_rms_gated_max, compute_crest_factor, compute_stereo_width, compute_stereo_imbalance, compute_log_spread |
|
|
| def apply_RMS_normalization(x, RMS_norm=-25, device=None, use_gate=False): |
| if device is None: |
| device = x.device |
|
|
| RMS= torch.tensor(RMS_norm, device=device).view(1, 1, 1).repeat(x.shape[0],1,1) |
|
|
| x_RMS_ref=20*torch.log10(torch.sqrt(torch.mean(x**2, dim=(-1), keepdim=True).mean(dim=-2, keepdim=True))) |
| if use_gate: |
| x_RMS = compute_log_rms_gated_max(x).unsqueeze(-1) |
| else: |
| x_RMS=20*torch.log10(torch.sqrt(torch.mean(x**2, dim=(-1), keepdim=True).mean(dim=-2, keepdim=True))) |
| |
|
|
| gain= RMS - x_RMS |
| gain_linear = 10 ** (gain / 20 + 1e-6) |
| x=x* gain_linear |
|
|
| return x |
|
|
|
|
| import pyloudnorm as pyln |
|
|
| def loudness_normalize(audio, target_loudness=-23.0, sample_rate=44100): |
| """ |
| Normalize the loudness of the audio to a target level. |
| """ |
|
|
| pylnmeter = pyln.Meter(sample_rate) |
|
|
| audio= np.array(audio, dtype=np.float32).T |
| loudness = pylnmeter.integrated_loudness(audio) |
|
|
|
|
| |
| loudness_normalized_audio = pyln.normalize.loudness(audio, loudness, -14.0) |
|
|
| return torch.tensor(loudness_normalized_audio.T, dtype=torch.float32) |
|
|