ashesh997's picture
Upload folder using huggingface_hub
d8ff165 verified
Raw History Blame Contribute Delete
5.86 kB
import torch
import torch.nn as nn
import torch.nn.functional as F
class ConvBlock(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size=(3, 3), stride=(2, 1), padding=(1, 1)):
super().__init__()
self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride=stride, padding=padding)
self.bn = nn.BatchNorm2d(out_channels)
self.act = nn.LeakyReLU(0.2)
def forward(self, x):
return self.act(self.bn(self.conv(x)))
class DeconvBlock(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size=(3, 3), stride=(2, 1), padding=(1, 1), output_padding=(1, 0)):
super().__init__()
self.deconv = nn.ConvTranspose2d(
in_channels, out_channels, kernel_size,
stride=stride, padding=padding, output_padding=output_padding
)
self.bn = nn.BatchNorm2d(out_channels)
self.act = nn.LeakyReLU(0.2)
def forward(self, x):
return self.act(self.bn(self.deconv(x)))
class ComplexCRNDenoiser(nn.Module):
"""
Complex Convolutional Recurrent Network (CRN) for Audio Noise Reduction.
Operates on complex STFT representations (Real + Imaginary) to preserve both
magnitude and phase, completely eliminating phase distortion.
"""
def __init__(
self,
n_fft=512,
hop_length=128,
win_length=512,
channels=[16, 32, 64, 128, 256],
rnn_hidden=256,
rnn_layers=2
):
super().__init__()
self.n_fft = n_fft
self.hop_length = hop_length
self.win_length = win_length
self.register_buffer("window", torch.hann_window(win_length))
# Encoder (Frequency downsampling by 2 at each step)
# Input shape: (B, 2, 256, T) -> channels[0] -> channels[1] -> ... -> channels[4]
# F: 256 -> 128 -> 64 -> 32 -> 16 -> 8
self.enc1 = ConvBlock(2, channels[0])
self.enc2 = ConvBlock(channels[0], channels[1])
self.enc3 = ConvBlock(channels[1], channels[2])
self.enc4 = ConvBlock(channels[2], channels[3])
self.enc5 = ConvBlock(channels[3], channels[4])
bottleneck_f = 8
self.rnn_in_dim = channels[4] * bottleneck_f
self.rnn = nn.GRU(
input_size=self.rnn_in_dim,
hidden_size=rnn_hidden,
num_layers=rnn_layers,
batch_first=True,
bidirectional=True
)
self.rnn_fc = nn.Linear(rnn_hidden * 2, self.rnn_in_dim)
# Decoder with Skip-Connections
self.dec5 = DeconvBlock(channels[4] * 2, channels[3])
self.dec4 = DeconvBlock(channels[3] * 2, channels[2])
self.dec3 = DeconvBlock(channels[2] * 2, channels[1])
self.dec2 = DeconvBlock(channels[1] * 2, channels[0])
self.dec1 = nn.ConvTranspose2d(
channels[0] * 2, 2, kernel_size=(3, 3), stride=(2, 1),
padding=(1, 1), output_padding=(1, 0)
)
def forward_stft(self, x):
# x is (B, 2, 256, T)
e1 = self.enc1(x) # (B, C0, 128, T)
e2 = self.enc2(e1) # (B, C1, 64, T)
e3 = self.enc3(e2) # (B, C2, 32, T)
e4 = self.enc4(e3) # (B, C3, 16, T)
e5 = self.enc5(e4) # (B, C4, 8, T)
B, C, F_dim, T_dim = e5.shape
# Reshape for GRU: (B, T, C * F)
rnn_in = e5.permute(0, 3, 1, 2).reshape(B, T_dim, C * F_dim)
rnn_out, _ = self.rnn(rnn_in)
rnn_proj = self.rnn_fc(rnn_out)
# Reshape back to (B, C, F, T)
d_in = rnn_proj.reshape(B, T_dim, C, F_dim).permute(0, 2, 3, 1)
# Skip connection decoding
d5 = self.dec5(torch.cat([d_in, e5], dim=1))
d4 = self.dec4(torch.cat([d5, e4], dim=1))
d3 = self.dec3(torch.cat([d4, e3], dim=1))
d2 = self.dec2(torch.cat([d3, e2], dim=1))
mask = self.dec1(torch.cat([d2, e1], dim=1)) # (B, 2, 256, T)
# Complex mask bounded via tanh
mask = torch.tanh(mask)
return mask
def forward(self, noisy_wav):
"""
Takes raw 1D audio waveform: (Batch, AudioSamples)
Returns cleaned 1D audio waveform: (Batch, AudioSamples)
"""
orig_len = noisy_wav.shape[-1]
if self.window.device != noisy_wav.device:
self.window = self.window.to(noisy_wav.device)
# 1. STFT
stft_res = torch.stft(
noisy_wav,
n_fft=self.n_fft,
hop_length=self.hop_length,
win_length=self.win_length,
window=self.window,
return_complex=True
) # (B, 257, T)
# Separate Real and Imaginary components
real = stft_res.real
imag = stft_res.imag
# Drop highest frequency bin (257 -> 256) for power-of-2 conv pooling
real_256 = real[:, :256, :]
imag_256 = imag[:, :256, :]
stft_in = torch.stack([real_256, imag_256], dim=1) # (B, 2, 256, T)
# 2. Predict Complex Mask (Mr, Mi)
mask = self.forward_stft(stft_in)
mask_r = mask[:, 0, :, :]
mask_i = mask[:, 1, :, :]
# 3. Complex multiplication: (Yr + j Yi) = (Xr + j Xi) * (Mr + j Mi)
# Yr = Xr * Mr - Xi * Mi
# Yi = Xr * Mi + Xi * Mr
clean_real_256 = real_256 * mask_r - imag_256 * mask_i
clean_imag_256 = real_256 * mask_i + imag_256 * mask_r
# Pad 257th frequency back with zeros
clean_real = F.pad(clean_real_256, (0, 0, 0, 1))
clean_imag = F.pad(clean_imag_256, (0, 0, 0, 1))
clean_stft = torch.complex(clean_real, clean_imag)
# 4. Inverse STFT to time domain
clean_wav = torch.istft(
clean_stft,
n_fft=self.n_fft,
hop_length=self.hop_length,
win_length=self.win_length,
window=self.window,
length=orig_len
)
return clean_wav