| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
|
|
| def wav_to_spec( |
| wav: torch.Tensor, |
| fft_size: int, |
| hop_length: int, |
| win_length: int, |
| window: torch.Tensor = None |
| ) -> tuple[torch.Tensor, torch.Tensor]: |
| """Convert waveform to magnitude and phase spectrograms. |
| |
| Args: |
| wav: Time-domain signal tensor of shape (batch, 1, samples) or (batch, samples). |
| fft_size: FFT window size. |
| hop_length: Hop length between frames. |
| win_length: Analysis window length. |
| window: Pre-created window tensor (optional). |
| |
| Returns: |
| magnitude: Magnitude spectrogram tensor. |
| phase: Phase spectrogram tensor. |
| """ |
| if wav.dim() == 3: |
| wav = wav.squeeze(1) |
| |
| if window is None: |
| window = torch.hann_window(win_length, device=wav.device) |
| |
| |
| stft = torch.stft( |
| wav, |
| n_fft=fft_size, |
| hop_length=hop_length, |
| win_length=win_length, |
| window=window, |
| center=True, |
| pad_mode='reflect', |
| normalized=False, |
| onesided=True, |
| return_complex=True |
| ) |
| |
| magnitude = torch.abs(stft) |
| phase = torch.angle(stft) |
| return magnitude, phase |
|
|
|
|
| def spec_to_wav( |
| magnitude: torch.Tensor, |
| phase: torch.Tensor, |
| fft_size: int, |
| hop_length: int, |
| win_length: int, |
| window: torch.Tensor = None |
| ) -> torch.Tensor: |
| """Convert magnitude and phase spectrograms back to time-domain waveform. |
| |
| Args: |
| magnitude: Magnitude spectrogram tensor. |
| phase: Phase spectrogram tensor. |
| fft_size: FFT window size. |
| hop_length: Hop length between frames. |
| win_length: Analysis window length. |
| window: Pre-created window tensor (optional). |
| |
| Returns: |
| wav: Reconstructed time-domain waveform of shape (batch, samples). |
| """ |
| if window is None: |
| window = torch.hann_window(win_length, device=magnitude.device) |
| |
| |
| stft = torch.polar(magnitude, phase) |
| |
| wav = torch.istft( |
| stft, |
| n_fft=fft_size, |
| hop_length=hop_length, |
| win_length=win_length, |
| window=window, |
| center=True, |
| normalized=False, |
| onesided=True |
| ) |
| |
| return wav.unsqueeze(1) |
|
|
|
|
| def pad_to_multiple(wav: torch.Tensor, multiple: int) -> torch.Tensor: |
| """Pad waveform time-dimension to be a multiple of a given divisor. |
| |
| Args: |
| wav: Tensor of shape (batch, channels, samples) or (batch, samples). |
| multiple: The integer the length should be a multiple of. |
| |
| Returns: |
| Padded tensor. |
| """ |
| length = wav.shape[-1] |
| remainder = length % multiple |
| if remainder == 0: |
| return wav |
| |
| pad_len = multiple - remainder |
| pad_left = pad_len // 2 |
| pad_right = pad_len - pad_left |
| |
| |
| return F.pad(wav, (pad_left, pad_right), mode='reflect') |
|
|