MEGAMI / utils /data_utils.py
Vansh Chugh
initial deploy
1a3ce68
Raw
History Blame Contribute Delete
6.34 kB
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.
"""
# Open the .wav 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:
# Read only the required frames
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) # Total number of frames
samplerate = audio_file.samplerate # Sample rate
duration = total_frames / samplerate # Duration in seconds
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:
#pad zeros to the right to make it 4 digits
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" # Return a default value if no mapping is found
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
# Get the size of the dimension
dim_size = x.size(dims)
# Handle shift larger than dimension size
shift = shift % dim_size
if shift < 0:
shift += dim_size
# Create indices for the roll
indices = torch.cat([torch.arange(dim_size-shift, dim_size),
torch.arange(0, dim_size-shift)])
# Use index_select for the roll
return torch.index_select(x, dims, indices)
#import loudness
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]) # Ensure x is 3D
B, C, T = x.shape
if device is None:
device = x.device
x_out = torch.zeros_like(x)
#for b in range(B):
# x_i=x[b].cpu().numpy().T
# lufs_in=loudness.integrated_loudness(x_i, sample_rate)
# delta_loudness= lufs - lufs_in
# gain=np.power(10, delta_loudness / 20) # Convert dB to linear gain
# x_out[b] = torch.tensor(x_i.T * gain, device=device)
x=x.view(B* C,1, T) # Ensure x is 3D
loudness=torchaudio.functional.loudness(x+1e-5, sample_rate=sample_rate)
delta_loudness = lufs - loudness
gain= torch.pow(10, delta_loudness / 20) # Convert dB to linear gain
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) # Apply gain to each channel
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) # Use fixed RMS for evaluation
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) # Convert dB gain to linear scale, adding a small value to avoid division by zero
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) # Create a meter for 44100 Hz sampling rate
audio= np.array(audio, dtype=np.float32).T
loudness = pylnmeter.integrated_loudness(audio)
# loudness normalize audio to -12 dB LUFS
loudness_normalized_audio = pyln.normalize.loudness(audio, loudness, -14.0)
return torch.tensor(loudness_normalized_audio.T, dtype=torch.float32)