szuweifu commited on
Commit
13e5029
·
verified ·
1 Parent(s): 7050837

Upload 14 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ enhanced_audio/offline_enhanced_mic_test.flac filter=lfs diff=lfs merge=lfs -text
37
+ enhanced_audio/online_enhanced_mic_test.flac filter=lfs diff=lfs merge=lfs -text
38
+ noisy_audio/mic_test.wav filter=lfs diff=lfs merge=lfs -text
39
+ offline_enhanced_audio/mic_test.flac filter=lfs diff=lfs merge=lfs -text
enhanced_audio/offline_enhanced_mic_test.flac ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d76e76de2dffcbb479646f594a6798bd9e84aa8a00196cbd7d1f3dccbbacfb3d
3
+ size 172529
enhanced_audio/online_enhanced_mic_test.flac ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fe038f9f531b60bf3e15f2660cf4bfc287a0d889e0e99f2ccca6a54203abb08f
3
+ size 172674
models/stfts.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
2
+ #
3
+ # NVIDIA CORPORATION and its licensors retain all intellectual property
4
+ # and proprietary rights in and to this software, related documentation
5
+ # and any modifications thereto. Any use, reproduction, disclosure or
6
+ # distribution of this software and related documentation without an express
7
+ # license agreement from NVIDIA CORPORATION is strictly prohibited.
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+
12
+ def decompress_signed_log1p(y):
13
+ return torch.sign(y) * (torch.expm1(torch.abs(y)))
14
+
15
+ RELU = nn.ReLU()
16
+
17
+ def mag_phase_stft(y, n_fft, hop_size, win_size, compress_factor=1.0, center=True, addeps=False):
18
+ """
19
+ Compute magnitude and phase using STFT.
20
+
21
+ Args:
22
+ y (torch.Tensor): Input audio signal.
23
+ n_fft (int): FFT size.
24
+ hop_size (int): Hop size.
25
+ win_size (int): Window size.
26
+ compress_factor (float, optional): Magnitude compression factor. Defaults to 1.0.
27
+ center (bool, optional): Whether to center the signal before padding. Defaults to True.
28
+ eps (bool, optional): Whether adding epsilon to magnitude and phase or not. Defaults to False.
29
+
30
+ Returns:
31
+ tuple: Magnitude, phase, and complex representation of the STFT.
32
+ """
33
+ eps = 1e-10
34
+ hann_window = torch.hann_window(win_size).to(y.device)
35
+ stft_spec = torch.stft(
36
+ y, n_fft,
37
+ hop_length=hop_size,
38
+ win_length=win_size,
39
+ window=hann_window,
40
+ center=center,
41
+ pad_mode='reflect',
42
+ normalized=False,
43
+ return_complex=True)
44
+
45
+ if addeps==False:
46
+ mag = torch.abs(stft_spec)
47
+ pha = torch.angle(stft_spec)
48
+ else:
49
+ real_part = stft_spec.real
50
+ imag_part = stft_spec.imag
51
+ mag = torch.sqrt(real_part.pow(2) + imag_part.pow(2) + eps)
52
+ pha = torch.atan2(imag_part + eps, real_part + eps)
53
+ # Compress the magnitude
54
+ if compress_factor in ['log1p','relu_log1p', 'signed_log1p']:
55
+ mag = torch.log1p(mag)
56
+ else:
57
+ mag = torch.pow(mag, compress_factor)
58
+ com = torch.stack((mag * torch.cos(pha), mag * torch.sin(pha)), dim=-1)
59
+ return mag, pha, com
60
+
61
+
62
+ def mag_phase_istft(mag, pha, n_fft, hop_size, win_size, compress_factor=1.0, center=True):
63
+ """
64
+ Inverse STFT to reconstruct the audio signal from magnitude and phase.
65
+
66
+ Args:
67
+ mag (torch.Tensor): Magnitude of the STFT.
68
+ pha (torch.Tensor): Phase of the STFT.
69
+ n_fft (int): FFT size.
70
+ hop_size (int): Hop size.
71
+ win_size (int): Window size.
72
+ compress_factor (float, optional): Magnitude compression factor. Defaults to 1.0.
73
+ center (bool, optional): Whether to center the signal before padding. Defaults to True.
74
+
75
+ Returns:
76
+ torch.Tensor: Reconstructed audio signal.
77
+ """
78
+ if compress_factor == 'log1p':
79
+ mag = torch.expm1(mag)
80
+ elif compress_factor == 'signed_log1p':
81
+ mag = decompress_signed_log1p(mag)
82
+ elif compress_factor == 'relu_log1p':
83
+ mag = torch.expm1(RELU(mag))
84
+ else:
85
+ mag = torch.pow(RELU(mag), 1.0 / compress_factor)
86
+ com = torch.complex(mag * torch.cos(pha), mag * torch.sin(pha))
87
+ hann_window = torch.hann_window(win_size).to(com.device)
88
+ wav = torch.istft(
89
+ com,
90
+ n_fft,
91
+ hop_length=hop_size,
92
+ win_length=win_size,
93
+ window=hann_window,
94
+ center=center)
95
+ return wav
models/streaming_codec_module_time_d1_input_ahead_sep_conv.py ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
2
+ #
3
+ # NVIDIA CORPORATION and its licensors retain all intellectual property
4
+ # and proprietary rights in and to this software, related documentation
5
+ # and any modifications thereto. Any use, reproduction, disclosure or
6
+ # distribution of this software and related documentation without an express
7
+ # license agreement from NVIDIA CORPORATION is strictly prohibited.
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+ import numpy as np
12
+ from einops import rearrange
13
+ import torch.nn.functional as F
14
+
15
+ class ChannelLayerNorm(nn.Module):
16
+ """
17
+ LayerNorm over channels only. Normalizes over C only.
18
+ Input shape: [B, C, T, F]
19
+ """
20
+ def __init__(self, num_channels):
21
+ super().__init__()
22
+ self.norm = nn.LayerNorm(num_channels)
23
+
24
+ def forward(self, x):
25
+ # [B, C, T, F] -> [B, T, F, C]
26
+ x = x.permute(0, 2, 3, 1)
27
+ # LayerNorm over C
28
+ x = self.norm(x)
29
+ # Back to [B, C, T, F]
30
+ x = x.permute(0, 3, 1, 2)
31
+ return x
32
+
33
+ def get_causal_padding_2d(kernel_size, dilation=(1,1)):
34
+ """
35
+ Causal padding only along time axis.
36
+ Frequency axis uses symmetric padding.
37
+ """
38
+ pad_t = kernel_size[0] * dilation[0] - dilation[0] # all padding on left side
39
+ pad_f = (kernel_size[1] * dilation[1] - dilation[1]) // 2
40
+ return (pad_f, pad_f, pad_t, 0) # (left, right, top, bottom)
41
+
42
+ def get_causal_padding_2d_FT(kernel_size, dilation=(1,1)):
43
+ """
44
+ Causal padding only along time axis.
45
+ Frequency axis uses symmetric padding.
46
+ """
47
+ pad_t = kernel_size[1] * dilation[1] - dilation[1] # all padding on left side
48
+ pad_f = (kernel_size[0] * dilation[0] - dilation[0]) // 2
49
+ return (pad_t, 0, pad_f, pad_f) # (left, right, top, bottom)
50
+
51
+ class SPConvTranspose2d(nn.Module):
52
+ def __init__(self, in_channels, out_channels, kernel_size, padding, r=1):
53
+ super(SPConvTranspose2d, self).__init__()
54
+ self.pad = nn.ConstantPad2d(padding, value=0.)
55
+ self.out_channels = out_channels
56
+ self.r = r
57
+ self.conv = nn.Conv2d(in_channels, out_channels * r, kernel_size=kernel_size, padding=0)
58
+
59
+
60
+ def forward(self, x):
61
+ x = self.pad(x)
62
+ out = self.conv(x)
63
+ batch_size, nchannels, H, W = out.shape
64
+ out = out.view((batch_size, self.r, nchannels // self.r, H, W))
65
+ out = out.permute(0, 2, 3, 4, 1)
66
+ out = out.contiguous().view((batch_size, nchannels // self.r, H, -1))
67
+ return out
68
+
69
+ class DenseBlock(nn.Module):
70
+ """
71
+ DenseBlock module consisting of multiple convolutional layers with dilation.
72
+ """
73
+ def __init__(self, cfg, kernel_size=(3, 3), depth=4):
74
+ super(DenseBlock, self).__init__()
75
+ self.cfg = cfg
76
+ self.depth = depth
77
+ self.hid_feature = cfg['model_cfg']['hid_feature']
78
+ self.dense_block = nn.ModuleList()
79
+
80
+ for i in range(depth):
81
+ dil = 2 ** i
82
+ pad = get_causal_padding_2d(kernel_size, (dil, 1))
83
+ dense_conv = nn.Sequential(
84
+ nn.ConstantPad2d(pad, 0.0),
85
+ nn.Conv2d(self.hid_feature * (i + 1), self.hid_feature, kernel_size,
86
+ dilation=(dil, 1), padding=0),
87
+ ChannelLayerNorm(self.hid_feature),
88
+ nn.PReLU(self.hid_feature)
89
+ )
90
+ self.dense_block.append(dense_conv)
91
+
92
+ def forward(self, x):
93
+ skip = x
94
+ for i in range(self.depth):
95
+ x = self.dense_block[i](skip)
96
+ skip = torch.cat([x, skip], dim=1)
97
+ return x
98
+
99
+ class DenseEncoder(nn.Module):
100
+ """
101
+ DenseEncoder module consisting of initial convolution, dense block, and a final convolution.
102
+ """
103
+ def __init__(self, cfg):
104
+ super(DenseEncoder, self).__init__()
105
+ self.cfg = cfg
106
+ self.input_channel = cfg['model_cfg']['input_channel']
107
+ self.hid_feature = cfg['model_cfg']['hid_feature']
108
+
109
+ self.dense_conv_1_1 = nn.Sequential(
110
+ nn.Conv2d(self.input_channel, self.hid_feature, (3, 3)),
111
+ ChannelLayerNorm(self.hid_feature),
112
+ nn.PReLU(self.hid_feature)
113
+ )
114
+
115
+ self.dense_conv_1_2 = nn.Sequential(
116
+ nn.Conv2d(self.input_channel, self.hid_feature, (3, 3)),
117
+ ChannelLayerNorm(self.hid_feature),
118
+ nn.PReLU(self.hid_feature)
119
+ )
120
+
121
+ self.dense_conv_1_3 = nn.Sequential(
122
+ nn.Conv2d(self.input_channel, self.hid_feature, (3, 3)),
123
+ ChannelLayerNorm(self.hid_feature),
124
+ nn.PReLU(self.hid_feature)
125
+ )
126
+
127
+ self.dense_block = DenseBlock(cfg, depth=4)
128
+
129
+ self.dense_conv_2 = nn.Sequential(
130
+ nn.Conv2d(self.hid_feature, self.hid_feature, (1, 3), stride=(1, 2)),
131
+ ChannelLayerNorm(self.hid_feature),
132
+ nn.PReLU(self.hid_feature)
133
+ )
134
+
135
+ def forward(self, x, number_ahead):
136
+ x = F.pad(x, (1,1, 2-number_ahead, number_ahead), "constant", 0) # (pad_f, pad_f, pad_t, 0) (left, right, top, bottom)
137
+ if number_ahead==0:
138
+ x = self.dense_conv_1_1(x) # [batch, hid_feature, time, freq]
139
+ elif number_ahead==1:
140
+ x = self.dense_conv_1_2(x) # [batch, hid_feature, time, freq]
141
+ elif number_ahead==2:
142
+ x = self.dense_conv_1_3(x) # [batch, hid_feature, time, freq]
143
+ else:
144
+ print('number_ahead not support!')
145
+
146
+ x = self.dense_block(x) # [batch, hid_feature, time, freq]
147
+ x = self.dense_conv_2(x) # [batch, hid_feature, time, freq//2]
148
+ return x
149
+
150
+ class MagDecoder(nn.Module):
151
+ """
152
+ MagDecoder module for decoding magnitude information.
153
+ """
154
+ def __init__(self, cfg):
155
+ super(MagDecoder, self).__init__()
156
+ self.dense_block = DenseBlock(cfg, depth=4)
157
+ self.hid_feature = cfg['model_cfg']['hid_feature']
158
+ self.output_channel = cfg['model_cfg']['output_channel']
159
+ self.n_fft = cfg['stft_cfg']['n_fft']
160
+ self.beta = cfg['model_cfg']['beta']
161
+
162
+ self.up_conv1 = nn.Sequential(
163
+ SPConvTranspose2d(self.hid_feature, self.hid_feature, (1, 3), get_causal_padding_2d((1,3)), 2),
164
+ ChannelLayerNorm(self.hid_feature),
165
+ nn.PReLU(self.hid_feature)
166
+ )
167
+
168
+ self.up_conv2 = nn.Sequential(
169
+ SPConvTranspose2d(self.hid_feature, self.hid_feature, (1, 3), get_causal_padding_2d_FT((1,3)), 1),
170
+ ChannelLayerNorm(self.hid_feature),
171
+ nn.PReLU(self.hid_feature)
172
+ )
173
+
174
+ self.final_conv = nn.Conv2d(self.hid_feature, self.output_channel, (1, 1))
175
+
176
+ def forward(self, x):
177
+ x = self.dense_block(x)
178
+ x = self.up_conv1(x)
179
+ x = self.up_conv2(x.permute(0,1,3,2)).permute(0,1,3,2)
180
+ x = self.final_conv(x)
181
+
182
+ return x
183
+
184
+ class PhaseDecoder(nn.Module):
185
+ """
186
+ PhaseDecoder module for decoding phase information.
187
+ """
188
+ def __init__(self, cfg):
189
+ super(PhaseDecoder, self).__init__()
190
+ self.dense_block = DenseBlock(cfg, depth=4)
191
+ self.hid_feature = cfg['model_cfg']['hid_feature']
192
+ self.output_channel = cfg['model_cfg']['output_channel']
193
+
194
+ self.up_conv1 = nn.Sequential(
195
+ SPConvTranspose2d(self.hid_feature, self.hid_feature, (1, 3), get_causal_padding_2d((1,3)), 2),
196
+ ChannelLayerNorm(self.hid_feature),
197
+ nn.PReLU(self.hid_feature)
198
+ )
199
+
200
+ self.up_conv2 = nn.Sequential(
201
+ SPConvTranspose2d(self.hid_feature, self.hid_feature, (1, 3), get_causal_padding_2d_FT((1,3)), 1),
202
+ ChannelLayerNorm(self.hid_feature),
203
+ nn.PReLU(self.hid_feature)
204
+ )
205
+
206
+ self.phase_conv_r = nn.Conv2d(self.hid_feature, self.output_channel, (1, 1))
207
+ self.phase_conv_i = nn.Conv2d(self.hid_feature, self.output_channel, (1, 1))
208
+
209
+ def forward(self, x):
210
+ x = self.dense_block(x)
211
+ x = self.up_conv1(x)
212
+ x = self.up_conv2(x.permute(0,1,3,2)).permute(0,1,3,2)
213
+ x_r = self.phase_conv_r(x)
214
+ x_i = self.phase_conv_i(x)
215
+ x = torch.atan2(x_i, x_r)
216
+ return x
models/streaming_generator_SEMamba_time_d1_random_layer_ahead_sep_conv.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
2
+ #
3
+ # NVIDIA CORPORATION and its licensors retain all intellectual property
4
+ # and proprietary rights in and to this software, related documentation
5
+ # and any modifications thereto. Any use, reproduction, disclosure or
6
+ # distribution of this software and related documentation without an express
7
+ # license agreement from NVIDIA CORPORATION is strictly prohibited.
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+ from einops import rearrange
12
+ from huggingface_hub import PyTorchModelHubMixin
13
+ from .streaming_mamba_block2_SEMamba import TFMambaBlock
14
+ from .streaming_codec_module_time_d1_input_ahead_sep_conv import DenseEncoder, MagDecoder, PhaseDecoder
15
+
16
+ class SEMamba_decoder_list(nn.Module, PyTorchModelHubMixin):
17
+ """
18
+ SEMamba model for speech enhancement using Mamba blocks.
19
+
20
+ This model uses a dense encoder, multiple Mamba blocks, and separate magnitude
21
+ and phase decoders to process noisy magnitude and phase inputs.
22
+ """
23
+ def __init__(self, cfg):
24
+ """
25
+ Initialize the SEMamba model.
26
+
27
+ Args:
28
+ - cfg: Configuration object containing model parameters.
29
+ """
30
+ super(SEMamba_decoder_list, self).__init__()
31
+ self.cfg = cfg
32
+ self.num_tscblocks = cfg['model_cfg']['num_tfmamba'] if cfg['model_cfg']['num_tfmamba'] is not None else 4 # default tfmamba: 4
33
+ self.mapping = cfg['model_cfg']['mapping'] if cfg['model_cfg']['mapping'] is not None else False # default tfmamba: 4
34
+
35
+ # Initialize dense encoder
36
+ self.dense_encoder = DenseEncoder(cfg)
37
+
38
+ # Initialize Mamba blocks
39
+ self.TSMamba = nn.ModuleList([TFMambaBlock(cfg) for _ in range(self.num_tscblocks)])
40
+
41
+ # Initialize decoders
42
+ self.mask_decoder = MagDecoder(cfg)
43
+ self.phase_decoder = PhaseDecoder(cfg)
44
+
45
+ self.mask_decoder_list = nn.ModuleList([MagDecoder(cfg) for _ in range(self.num_tscblocks)])
46
+ self.phase_decoder_list = nn.ModuleList([PhaseDecoder(cfg) for _ in range(self.num_tscblocks)])
47
+
48
+ def forward(self, noisy_mag, noisy_pha, layer_use, number_ahead):
49
+ """
50
+ Forward pass for the SEMamba model.
51
+
52
+ Args:
53
+ - noisy_mag (torch.Tensor): Noisy magnitude input tensor [B, F, T].
54
+ - noisy_pha (torch.Tensor): Noisy phase input tensor [B, F, T].
55
+
56
+ Returns:
57
+ - denoised_mag (torch.Tensor): Denoised magnitude tensor [B, F, T].
58
+ - denoised_pha (torch.Tensor): Denoised phase tensor [B, F, T].
59
+ - denoised_com (torch.Tensor): Denoised complex tensor [B, F, T, 2].
60
+ """
61
+ # Reshape inputs
62
+ noisy_mag = rearrange(noisy_mag, 'b f t -> b t f').unsqueeze(1) # [B, 1, T, F]
63
+ noisy_pha = rearrange(noisy_pha, 'b f t -> b t f').unsqueeze(1) # [B, 1, T, F]
64
+
65
+ # Concatenate magnitude and phase inputs
66
+ x = torch.cat((noisy_mag, noisy_pha), dim=1) # [B, 2, T, F]
67
+
68
+ # Prevent unpredictable errors
69
+ B, C, T, F = x.shape
70
+ zeros = torch.zeros(B, C, T, 2, device=x.device)
71
+ x = torch.cat((x, zeros), dim=-1)
72
+
73
+ # Encode input
74
+ x = self.dense_encoder(x, number_ahead)
75
+
76
+ # Apply Mamba blocks
77
+ for block in self.TSMamba[0:layer_use]:
78
+ x = block(x)
79
+
80
+ denoised_mag = rearrange(self.mask_decoder_list[layer_use-1](x), 'b c t f -> b f t c').squeeze(-1)
81
+ denoised_pha = rearrange(self.phase_decoder_list[layer_use-1](x), 'b c t f -> b f t c').squeeze(-1)
82
+
83
+ # Prevent unpredictable errors
84
+ denoised_mag = denoised_mag[:, :F, :T]
85
+ denoised_pha = denoised_pha[:, :F, :T]
86
+
87
+ # Combine denoised magnitude and phase into a complex representation
88
+ denoised_com = torch.stack(
89
+ (denoised_mag * torch.cos(denoised_pha), denoised_mag * torch.sin(denoised_pha)),
90
+ dim=-1
91
+ )
92
+
93
+ return denoised_mag, denoised_pha, denoised_com
models/streaming_mamba_block2_SEMamba.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
2
+ #
3
+ # NVIDIA CORPORATION and its licensors retain all intellectual property
4
+ # and proprietary rights in and to this software, related documentation
5
+ # and any modifications thereto. Any use, reproduction, disclosure or
6
+ # distribution of this software and related documentation without an express
7
+ # license agreement from NVIDIA CORPORATION is strictly prohibited.
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+ import torch.nn.functional as F
12
+ from torch.nn import init
13
+ from torch.nn.parameter import Parameter
14
+ from functools import partial
15
+ from einops import rearrange
16
+ from mamba_ssm import Mamba
17
+
18
+ class Causal_MambaBlock(nn.Module):
19
+ def __init__(self, d_model, cfg):
20
+ super(Causal_MambaBlock, self).__init__()
21
+
22
+ d_state = cfg['model_cfg']['d_state'] # 16
23
+ d_conv = cfg['model_cfg']['d_conv'] # 4
24
+ expand = cfg['model_cfg']['expand'] # 4
25
+
26
+ self.forward_blocks = Mamba(d_model=d_model, d_state=d_state, d_conv=d_conv, expand=expand)
27
+ self.output_proj = nn.Linear(d_model, d_model)
28
+ self.norm = nn.LayerNorm(d_model)
29
+
30
+ def forward(self, x):
31
+ # x: [B, T, D]
32
+ out_fw = self.forward_blocks(x) + x
33
+ out = self.output_proj(out_fw)
34
+
35
+ # LayerNorm
36
+ return self.norm(out)
37
+
38
+ class MambaBlock(nn.Module):
39
+ def __init__(self, d_model, cfg):
40
+ super(MambaBlock, self).__init__()
41
+
42
+ d_state = cfg['model_cfg']['d_state'] # 16
43
+ d_conv = cfg['model_cfg']['d_conv'] # 4
44
+ expand = cfg['model_cfg']['expand'] # 4
45
+
46
+ self.forward_blocks = Mamba(d_model=d_model, d_state=d_state, d_conv=d_conv, expand=expand)
47
+ self.backward_blocks = Mamba(d_model=d_model, d_state=d_state, d_conv=d_conv, expand=expand)
48
+ self.output_proj = nn.Linear(2 * d_model, d_model)
49
+ self.norm = nn.LayerNorm(d_model)
50
+
51
+ def forward(self, x):
52
+ # x: [B, T, D]
53
+ out_fw = self.forward_blocks(x) + x
54
+
55
+ out_bw = self.backward_blocks(torch.flip(x, dims=[1])) + torch.flip(x, dims=[1])
56
+ out_bw = torch.flip(out_bw, dims=[1])
57
+
58
+ out = torch.cat([out_fw, out_bw], dim=-1)
59
+ out = self.output_proj(out)
60
+
61
+ # LayerNorm
62
+ return self.norm(out)
63
+
64
+
65
+ class TFMambaBlock(nn.Module):
66
+ """
67
+ Temporal-Frequency Mamba block for sequence modeling.
68
+
69
+ Attributes:
70
+ cfg (Config): Configuration for the block.
71
+ time_mamba (MambaBlock): Mamba block for temporal dimension.
72
+ freq_mamba (MambaBlock): Mamba block for frequency dimension.
73
+ tlinear (ConvTranspose1d): ConvTranspose1d layer for temporal dimension.
74
+ flinear (ConvTranspose1d): ConvTranspose1d layer for frequency dimension.
75
+ """
76
+ def __init__(self, cfg):
77
+ super(TFMambaBlock, self).__init__()
78
+ self.cfg = cfg
79
+ self.hid_feature = cfg['model_cfg']['hid_feature']
80
+
81
+ # Initialize Mamba blocks
82
+ self.time_mamba = Causal_MambaBlock(d_model=self.hid_feature, cfg=cfg)
83
+ self.freq_mamba = MambaBlock(d_model=self.hid_feature, cfg=cfg)
84
+
85
+ def forward(self, x):
86
+ """
87
+ Forward pass of the TFMamba block.
88
+
89
+ Parameters:
90
+ x (Tensor): Input tensor with shape (batch, channels, time, freq).
91
+
92
+ Returns:
93
+ Tensor: Output tensor after applying temporal and frequency Mamba blocks.
94
+ """
95
+ b, c, t, f = x.size()
96
+ x = x.permute(0, 3, 2, 1).contiguous().view(b*f, t, c)
97
+ x = self.time_mamba(x) + x
98
+ x = x.view(b, f, t, c).permute(0, 2, 1, 3).contiguous().view(b*t, f, c)
99
+ x = self.freq_mamba(x) + x
100
+ x = x.view(b, t, f, c).permute(0, 3, 1, 2)
101
+ return x
102
+
noisy_audio/mic_test.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:dd54fe1decefe2e5c11c489f3d3d5b0e567bdd840f71bf4007cbe6349194f794
3
+ size 571580
offline_enhanced_audio/mic_test.flac ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d76e76de2dffcbb479646f594a6798bd9e84aa8a00196cbd7d1f3dccbbacfb3d
3
+ size 172529
offline_inference.py ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
2
+ #
3
+ # NVIDIA CORPORATION and its licensors retain all intellectual property
4
+ # and proprietary rights in and to this software, related documentation
5
+ # and any modifications thereto. Any use, reproduction, disclosure or
6
+ # distribution of this software and related documentation without an express
7
+ # license agreement from NVIDIA CORPORATION is strictly prohibited.
8
+
9
+ import os
10
+ import argparse
11
+ import torch
12
+ import torchaudio
13
+ import torch.nn as nn
14
+ import librosa
15
+ from models.stfts import mag_phase_stft, mag_phase_istft
16
+ from models.streaming_generator_SEMamba_time_d1_random_layer_ahead_sep_conv import SEMamba_decoder_list
17
+ from utils.util import load_config, pad_or_trim_to_match
18
+ from huggingface_hub import hf_hub_download
19
+ RELU = nn.ReLU()
20
+
21
+ config_path = hf_hub_download(repo_id="nvidia/Real-time_RE-USE", filename="config.json")
22
+
23
+ def get_filepaths(directory, file_type=None):
24
+ file_paths = [] # List which will store all of the full filepaths.
25
+ # Walk the tree.
26
+ for root, directories, files in os.walk(directory):
27
+ for filename in files:
28
+ # Join the two strings in order to form the full filepath.
29
+ filepath = os.path.join(root, filename)
30
+ if file_type is not None:
31
+ if filepath.split('.')[-1] == file_type:
32
+ file_paths.append(filepath) # Add it to the list.
33
+ else:
34
+ file_paths.append(filepath) # Add it to the list.
35
+ return file_paths # Self-explanatory.
36
+
37
+ def make_even(value):
38
+ value = int(round(value))
39
+ return value if value % 2 == 0 else value + 1
40
+
41
+ def inference(args, device):
42
+ cfg = load_config(args.config)
43
+ n_fft, hop_size, win_size = cfg['stft_cfg']['n_fft'], cfg['stft_cfg']['hop_size'], cfg['stft_cfg']['win_size']
44
+ compress_factor = cfg['model_cfg']['compress_factor']
45
+ sampling_rate = cfg['stft_cfg']['sampling_rate']
46
+
47
+ SE_model = SEMamba_decoder_list.from_pretrained("nvidia/Real-time_RE-USE", cfg=cfg).to(device)
48
+ SE_model.eval()
49
+
50
+ os.makedirs(args.output_folder, exist_ok=True)
51
+ with torch.no_grad():
52
+ for i, fname in enumerate(get_filepaths(args.input_folder)):
53
+ print(fname)
54
+ try:
55
+ os.makedirs(args.output_folder + fname[0:fname.rfind('/')].replace(args.input_folder,''), exist_ok=True)
56
+ noisy_wav, noisy_sr = torchaudio.load(fname)
57
+ except Exception as e:
58
+ print(f"Warning: cannot read {fname}, skipping. ({e})")
59
+ continue
60
+
61
+ if args.BWE is not None:
62
+ opts = {"res_type": "kaiser_best"}
63
+ noisy_wav = librosa.resample(noisy_wav.cpu().numpy(), orig_sr=noisy_sr, target_sr=int(args.BWE), **opts)
64
+ noisy_sr = int(args.BWE)
65
+
66
+ noisy_wav = torch.FloatTensor(noisy_wav).to(device)
67
+ n_fft_scaled = make_even(n_fft * noisy_sr // sampling_rate)
68
+ hop_size_scaled = make_even(hop_size * noisy_sr // sampling_rate)
69
+ win_size_scaled = make_even(win_size * noisy_sr // sampling_rate)
70
+
71
+ noisy_mag, noisy_pha, noisy_com = mag_phase_stft(
72
+ noisy_wav,
73
+ n_fft=n_fft_scaled,
74
+ hop_size=hop_size_scaled,
75
+ win_size=win_size_scaled,
76
+ compress_factor=compress_factor,
77
+ center=True,
78
+ addeps=False
79
+ )
80
+
81
+ # Offline inference!!
82
+ amp_g, pha_g, _ = SE_model(noisy_mag, noisy_pha, args.Exit_layer, args.look_ahead_frames)
83
+ # To remove "strange sweep artifact"
84
+ mag = torch.expm1(RELU(amp_g)) # [1, F, T]
85
+ zero_portion = torch.sum(mag==0, 1)/mag.shape[1]
86
+ amp_g[:,:,(zero_portion>0.5)[0]] = 0
87
+
88
+ audio_g = mag_phase_istft(amp_g, pha_g, n_fft_scaled, hop_size_scaled, win_size_scaled, compress_factor)
89
+ audio_g = pad_or_trim_to_match(noisy_wav.detach(), audio_g, pad_value=1e-8) # Align lengths using epsilon padding
90
+ assert audio_g.shape == noisy_wav.shape, audio_g.shape
91
+
92
+ output_file = os.path.join(args.output_folder + fname.replace(args.input_folder,'').split('.')[0]+'.flac') # save to .flac format
93
+ torchaudio.save(output_file, audio_g.cpu(), noisy_sr)
94
+
95
+ def main():
96
+ print('Initializing Inference Process...')
97
+ parser = argparse.ArgumentParser()
98
+ parser.add_argument('--input_folder')
99
+ parser.add_argument('--output_folder')
100
+ parser.add_argument('--config')
101
+ parser.add_argument('--Exit_layer', type=int, required=True)
102
+ parser.add_argument('--look_ahead_frames', type=int, required=True)
103
+ parser.add_argument('--BWE', default=None)
104
+ args = parser.parse_args()
105
+
106
+ global device
107
+ if torch.cuda.is_available():
108
+ device = torch.device('cuda')
109
+ else:
110
+ raise RuntimeError("Currently, CPU mode is not supported.")
111
+
112
+ inference(args, device)
113
+
114
+
115
+ if __name__ == '__main__':
116
+ main()
offline_inference.sh ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ CUDA_VISIBLE_DEVICES='0' python offline_inference.py \
2
+ --input_folder ./noisy_audio \
3
+ --output_folder ./offline_enhanced_audio \
4
+ --config recipes/USEMamba_12x1_lr_00002_norm_05_vq_067_nfft_320_hop_160_NRIR_012_pha_0005_com_04_early_005_release_random_layer_GAN_longer_1k.yaml \
5
+ --Exit_layer 8 \
6
+ --look_ahead_frames 0 \
7
+ #--BWE 16000 \
8
+ # Exit_layer can be between 3~12 , look_ahead_frames can be between 0~2
online_inference.py ADDED
@@ -0,0 +1,470 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
2
+ #
3
+ # NVIDIA CORPORATION and its licensors retain all intellectual property
4
+ # and proprietary rights in and to this software, related documentation
5
+ # and any modifications thereto. Any use, reproduction, disclosure or
6
+ # distribution of this software and related documentation without an express
7
+ # license agreement from NVIDIA CORPORATION is strictly prohibited.
8
+
9
+ import torch
10
+ import torchaudio
11
+ import torchaudio.transforms as T
12
+ import torch.nn.functional as F
13
+ import torch.nn as nn
14
+ import argparse
15
+ import librosa
16
+ import numpy as np
17
+ import time
18
+ from models.stfts import mag_phase_stft, mag_phase_istft
19
+ from models.streaming_generator_SEMamba_time_d1_random_layer_ahead_sep_conv import SEMamba_decoder_list
20
+ from utils.util import load_config, pad_or_trim_to_match
21
+ from huggingface_hub import hf_hub_download
22
+ RELU = nn.ReLU()
23
+
24
+ config_path = hf_hub_download(repo_id="nvidia/Real-time_RE-USE", filename="config.json")
25
+ ################### Streaming inference modification start ######################################
26
+ def get_causal_padding_2d(kernel_size, dilation=(1,1)):
27
+ """
28
+ Causal padding only along time axis.
29
+ Frequency axis uses symmetric padding.
30
+ """
31
+ pad_t = kernel_size[0] * dilation[0] - dilation[0] # all padding on left side
32
+ pad_f = (kernel_size[1] * dilation[1] - dilation[1]) // 2
33
+ return (pad_f, pad_f, pad_t, 0) # (left, right, top, bottom)
34
+
35
+ class StreamingCausalConv2d(nn.Module):
36
+ def __init__(self, conv: nn.Conv2d):
37
+ super().__init__()
38
+ self.conv = conv
39
+ kt, kf = conv.kernel_size
40
+ dt, df = conv.dilation
41
+ self.cache_len = (kt - 1) * dt
42
+ self.register_buffer("cache", None, persistent=False)
43
+ self.freq_pad = get_causal_padding_2d((1, kf), (dt, df))
44
+
45
+ def reset(self):
46
+ self.cache = None
47
+
48
+ def forward(self, x, t_pad=None):
49
+ # x: [B, C, 1, f]
50
+ B, C, _, f = x.shape
51
+
52
+ if self.cache is None:
53
+ # Assign the instance variable if no specific pad is provided
54
+ if t_pad is None:
55
+ t_pad = self.cache_len
56
+ self.cache = torch.zeros(
57
+ B, C, t_pad, f,
58
+ device=x.device,
59
+ dtype=x.dtype
60
+ )
61
+
62
+ x_cat = torch.cat([self.cache, x], dim=2)
63
+ y = self.conv(F.pad(x_cat, self.freq_pad, "constant", 0))
64
+ self.cache = x_cat[:, :, -self.cache_len:, :]
65
+ return y
66
+
67
+ class StreamingCausalConv2d_FT(nn.Module):
68
+ def __init__(self, conv: nn.Conv2d):
69
+ super().__init__()
70
+ self.conv = conv
71
+ kf, kt = conv.kernel_size
72
+ df, dt = conv.dilation
73
+ self.cache_len = (kt - 1) * dt
74
+ self.register_buffer("cache", None, persistent=False)
75
+ #self.freq_pad = get_causal_padding_2d_FT((kf, 1), (df, dt))
76
+
77
+ def reset(self):
78
+ self.cache = None
79
+
80
+ def forward(self, x):
81
+ # x: [B, C, f, 1]
82
+ B, C, f, _ = x.shape
83
+
84
+ if self.cache is None:
85
+ self.cache = torch.zeros(
86
+ B, C, f, self.cache_len,
87
+ device=x.device,
88
+ dtype=x.dtype
89
+ )
90
+ x_cat = torch.cat([self.cache, x], dim=3)
91
+ y = self.conv(x_cat)
92
+ self.cache = x_cat[:, :, :, -self.cache_len:]
93
+ return y
94
+
95
+ class StreamingDenseBlock(nn.Module):
96
+ def __init__(self, dense_block):
97
+ super().__init__()
98
+ self.blocks = nn.ModuleList()
99
+
100
+ for seq in dense_block.dense_block:
101
+ conv = seq[1]
102
+ self.blocks.append(nn.Sequential(
103
+ StreamingCausalConv2d(conv),
104
+ seq[2], # ChannelLayerNorm
105
+ seq[3], # PReLU
106
+ ))
107
+
108
+ def reset(self):
109
+ for b in self.blocks:
110
+ b[0].reset()
111
+
112
+ def forward(self, x):
113
+ skip = x
114
+ for b in self.blocks:
115
+ y = b(skip)
116
+ skip = torch.cat([y, skip], dim=1)
117
+ return y
118
+
119
+ class StreamingDenseEncoder(nn.Module):
120
+ def __init__(self, encoder):
121
+ super().__init__()
122
+
123
+ # dense_conv_1_1
124
+ conv1_1 = encoder.dense_conv_1_1[0]
125
+ self.conv1_1 = nn.Sequential(
126
+ StreamingCausalConv2d(conv1_1),
127
+ encoder.dense_conv_1_1[1],
128
+ encoder.dense_conv_1_1[2],
129
+ )
130
+
131
+ # dense_conv_1_2
132
+ conv1_2 = encoder.dense_conv_1_2[0]
133
+ self.conv1_2 = nn.Sequential(
134
+ StreamingCausalConv2d(conv1_2),
135
+ encoder.dense_conv_1_2[1],
136
+ encoder.dense_conv_1_2[2],
137
+ )
138
+
139
+ # dense_conv_1_3
140
+ conv1_3 = encoder.dense_conv_1_3[0]
141
+ self.conv1_3 = nn.Sequential(
142
+ StreamingCausalConv2d(conv1_3),
143
+ encoder.dense_conv_1_3[1],
144
+ encoder.dense_conv_1_3[2],
145
+ )
146
+
147
+ self.dense_block = StreamingDenseBlock(encoder.dense_block)
148
+
149
+ # time kernel = 1 → no cache needed
150
+ self.conv2 = encoder.dense_conv_2
151
+
152
+ def reset(self):
153
+ self.conv1_1[0].reset()
154
+ self.conv1_2[0].reset()
155
+ self.conv1_3[0].reset()
156
+ self.dense_block.reset()
157
+
158
+ def forward(self, x, look_ahead_frames):
159
+ # x: [B, C, 1, F]
160
+ #x = F.pad(x, (1, 1, 2-number_ahead, number_ahead), "constant", 0)
161
+ if look_ahead_frames == 0:
162
+ seq, pad = self.conv1_1, 2
163
+ elif look_ahead_frames == 1:
164
+ seq, pad = self.conv1_2, 1
165
+ elif look_ahead_frames == 2:
166
+ seq, pad = self.conv1_3, 0
167
+ else:
168
+ print('look_ahead_frames not support!')
169
+
170
+ # Manually pipe through the first layer with the extra argument
171
+ # seq[0] is the StreamingCausalConv2d
172
+ x = seq[0](x, t_pad=pad)
173
+
174
+ # Pass through the remaining layers in the Sequential
175
+ for i in range(1, len(seq)):
176
+ x = seq[i](x)
177
+
178
+ x = self.dense_block(x)
179
+ x = self.conv2(x)
180
+ return x
181
+
182
+ class StreamingMagDecoder(nn.Module):
183
+ def __init__(self, decoder):
184
+ super().__init__()
185
+
186
+ self.up_conv1 = decoder.up_conv1
187
+ self.up_conv2 = nn.Sequential(
188
+ StreamingCausalConv2d_FT(decoder.up_conv2[0].conv),
189
+ decoder.up_conv2[1],
190
+ decoder.up_conv2[2],
191
+ )
192
+ self.final_conv = decoder.final_conv
193
+ self.dense_block = StreamingDenseBlock(decoder.dense_block)
194
+
195
+ def reset(self):
196
+ self.dense_block.reset()
197
+ self.up_conv2[0].reset()
198
+
199
+ def forward(self, x):
200
+ # x: [B, C, 1, F]
201
+ x = self.dense_block(x)
202
+ x = self.up_conv1(x)
203
+ x = self.up_conv2(x.permute(0,1,3,2)).permute(0,1,3,2)
204
+ x = self.final_conv(x)
205
+ return x
206
+
207
+ class StreamingPhaseDecoder(nn.Module):
208
+ def __init__(self, decoder):
209
+ super().__init__()
210
+
211
+ self.up_conv1 = decoder.up_conv1
212
+ self.up_conv2 = nn.Sequential(
213
+ StreamingCausalConv2d_FT(decoder.up_conv2[0].conv),
214
+ decoder.up_conv2[1],
215
+ decoder.up_conv2[2],
216
+ )
217
+ self.phase_conv_r = decoder.phase_conv_r
218
+ self.phase_conv_i = decoder.phase_conv_i
219
+ self.dense_block = StreamingDenseBlock(decoder.dense_block)
220
+
221
+ def reset(self):
222
+ self.dense_block.reset()
223
+ self.up_conv2[0].reset()
224
+
225
+ def forward(self, x):
226
+ # x: [B, C, 1, F]
227
+ x = self.dense_block(x)
228
+ x = self.up_conv1(x)
229
+ x = self.up_conv2(x.permute(0,1,3,2)).permute(0,1,3,2)
230
+ x_r = self.phase_conv_r(x)
231
+ x_i = self.phase_conv_i(x)
232
+ x = torch.atan2(x_i, x_r)
233
+ return x
234
+
235
+ class StreamingCausalMambaBlock(nn.Module):
236
+ def __init__(self, block):
237
+ super().__init__()
238
+ self.mamba = block.forward_blocks
239
+ self.proj = block.output_proj
240
+ self.norm = block.norm
241
+ self.register_buffer("conv_state", None, persistent=False)
242
+ self.register_buffer("ssm_state", None, persistent=False)
243
+
244
+ def reset(self, batch_size):
245
+ self.conv_state, self.ssm_state = self.mamba.allocate_inference_cache(
246
+ batch_size=batch_size, # Frequecy_dim
247
+ max_seqlen=1,
248
+ )
249
+
250
+ def forward(self, x):
251
+ """
252
+ x: [B, 1, D]
253
+ """
254
+ y, self.conv_state, self.ssm_state = self.mamba.step(x, self.conv_state, self.ssm_state)
255
+ y = y + x
256
+ y = self.proj(y)
257
+ return self.norm(y)
258
+
259
+ class StreamingTFMambaBlock(nn.Module):
260
+ def __init__(self, block):
261
+ super().__init__()
262
+ self.time_mamba = StreamingCausalMambaBlock(block.time_mamba)
263
+ self.freq_mamba = block.freq_mamba # freq is full, no streaming needed
264
+
265
+ def reset(self, batch_size):
266
+ self.time_mamba.reset(batch_size)
267
+
268
+ def forward(self, x):
269
+ # x: [B, C, 1, F]
270
+ B, C, _, F = x.shape
271
+
272
+ # ---- Time Mamba (true streaming) ----
273
+ x_t = x.permute(0, 3, 2, 1).reshape(B * F, 1, C)
274
+ x_t = self.time_mamba(x_t) + x_t
275
+ x_t = x_t.view(B, F, 1, C).permute(0, 3, 2, 1)
276
+
277
+ # ---- Frequency Mamba (non-causal, full freq) ----
278
+ x_f = x_t.permute(0, 2, 3, 1).reshape(B, F, C)
279
+ x_f = self.freq_mamba(x_f) + x_f
280
+ x_f = x_f.view(B, 1, F, C).permute(0, 3, 1, 2)
281
+
282
+ return x_f
283
+
284
+ class StreamingSEMamba(nn.Module):
285
+ def __init__(self, model):
286
+ super().__init__()
287
+ self.encoder = StreamingDenseEncoder(model.dense_encoder)
288
+ self.mamba = nn.ModuleList(
289
+ [StreamingTFMambaBlock(b) for b in model.TSMamba]
290
+ )
291
+ self.mag_decoder_list = nn.ModuleList([StreamingMagDecoder(decoder) for decoder in model.mask_decoder_list])
292
+ self.pha_decoder_list = nn.ModuleList([StreamingPhaseDecoder(decoder) for decoder in model.phase_decoder_list])
293
+
294
+ def reset(self, freq_dim):
295
+ self.encoder.reset()
296
+ for l in self.mamba:
297
+ l.reset(freq_dim)
298
+ for dec in self.mag_decoder_list:
299
+ dec.reset()
300
+ for dec in self.pha_decoder_list:
301
+ dec.reset()
302
+
303
+ def forward(self, noisy_mag_t, noisy_pha_t, layer_use, look_ahead_frames):
304
+ """
305
+ noisy_mag_t: [B, F, T]
306
+ noisy_pha_t: [B, F, T]
307
+ """
308
+ x_mag = noisy_mag_t.permute(0,2,1).unsqueeze(1) # [B,1,T,F]
309
+ x_pha = noisy_pha_t.permute(0,2,1).unsqueeze(1) # [B,1,T,F]
310
+ x = torch.cat([x_mag, x_pha], dim=1) # [B,2,T,F]
311
+
312
+ # match original zero-padding logic
313
+ zeros = torch.zeros(x.size(0), x.size(1), x.size(2), 2, device=x.device)
314
+ x = torch.cat([x, zeros], dim=-1)
315
+
316
+ # ---- Encoder ----
317
+ x = self.encoder(x, look_ahead_frames)
318
+
319
+ # ---- Mamba ----
320
+ #import pdb; pdb.set_trace()
321
+ for b in self.mamba[0:layer_use]:
322
+ x = b(x)
323
+
324
+ # ---- Decoders ----
325
+ mag = self.mag_decoder_list[layer_use-1](x)
326
+ pha = self.pha_decoder_list[layer_use-1](x)
327
+
328
+ mag = mag.squeeze(2).squeeze(1) # [B,F]
329
+ pha = pha.squeeze(2).squeeze(1)
330
+
331
+ # Prevent unpredictable errors
332
+ mag = mag[:, 0:x_mag.shape[-1]]
333
+ pha = pha[:, 0:x_mag.shape[-1]]
334
+
335
+ return mag, pha
336
+
337
+ ################### Streaming inference modification end ######################################
338
+
339
+ def make_even(value):
340
+ value = int(round(value))
341
+ return value if value % 2 == 0 else value + 1
342
+
343
+ def inference(args, device):
344
+ cfg = load_config(args.config)
345
+ n_fft, hop_size, win_size = cfg['stft_cfg']['n_fft'], cfg['stft_cfg']['hop_size'], cfg['stft_cfg']['win_size']
346
+ compress_factor = cfg['model_cfg']['compress_factor']
347
+ sampling_rate = cfg['stft_cfg']['sampling_rate']
348
+
349
+ model = SEMamba_decoder_list.from_pretrained("nvidia/Real-time_RE-USE", cfg=cfg).to(device)
350
+ model.eval()
351
+ streaming_model = StreamingSEMamba(model).eval()
352
+
353
+ with torch.no_grad():
354
+ noisy_wav, noisy_sr = torchaudio.load('./noisy_audio/mic_test.wav')
355
+
356
+ # Leave online BWE as future work:
357
+ if args.BWE is not None:
358
+ opts = {"res_type": "kaiser_fast"}
359
+ noisy_wav = librosa.resample(noisy_wav.cpu().numpy(), orig_sr=noisy_sr, target_sr=int(args.BWE), **opts)
360
+ noisy_sr = int(args.BWE)
361
+ noisy_wav = torch.FloatTensor(noisy_wav).to(device)
362
+
363
+ n_fft_scaled = make_even(n_fft * noisy_sr // sampling_rate)
364
+ hop_size_scaled = make_even(hop_size * noisy_sr // sampling_rate)
365
+ win_size_scaled = make_even(win_size * noisy_sr // sampling_rate)
366
+
367
+ # Leave online STFT as future work:
368
+ noisy_mag, noisy_pha, noisy_com = mag_phase_stft( # (B, F, T)
369
+ noisy_wav,
370
+ n_fft=n_fft_scaled,
371
+ hop_size=hop_size_scaled,
372
+ win_size=win_size_scaled,
373
+ compress_factor=compress_factor,
374
+ center=True,
375
+ addeps=False
376
+ )
377
+
378
+ ## Offline inference!!
379
+ mag_out2, pha_out2, _ = model(noisy_mag, noisy_pha, args.Exit_layer, args.look_ahead_frames)
380
+ # To remove "strange sweep artifact"
381
+ mag2 = torch.expm1(RELU(mag_out2)) # [1, F, T]
382
+ zero_portion = torch.sum(mag2==0, 1)/mag2.shape[1]
383
+ mag_out2[:,:,(zero_portion>0.5)[0]] = 0
384
+ audio_g2 = mag_phase_istft(mag_out2, pha_out2, n_fft_scaled, hop_size_scaled, win_size_scaled, compress_factor)
385
+ audio_g2 = pad_or_trim_to_match(noisy_wav.detach(), audio_g2, pad_value=1e-8) # Align lengths using epsilon padding
386
+ torchaudio.save('./enhanced_audio/offline_enhanced_mic_test.flac', audio_g2.cpu(), noisy_sr)
387
+
388
+ ## Online inference!! (one frame in, one frame out)
389
+ streaming_model.reset(noisy_mag.shape[1]//2+1)
390
+ noisy_mag = torch.cat([noisy_mag, torch.zeros(noisy_mag.shape[0],noisy_mag.shape[1], args.look_ahead_frames, device=noisy_mag.device)], dim=-1)
391
+ noisy_pha = torch.cat([noisy_pha, torch.zeros(noisy_pha.shape[0],noisy_pha.shape[1], args.look_ahead_frames, device=noisy_pha.device)], dim=-1)
392
+
393
+ times, mag_out, pha_out = [], [], []
394
+ print('Start Online inferencing...')
395
+ # 1. wait for the enough look_ahead_frames
396
+ start = time.perf_counter()
397
+ mag_t, pha_t = streaming_model(
398
+ noisy_mag[:, :, 0:args.look_ahead_frames+1],
399
+ noisy_pha[:, :, 0:args.look_ahead_frames+1],
400
+ args.Exit_layer,
401
+ args.look_ahead_frames
402
+ )
403
+ torch.cuda.synchronize()
404
+ end = time.perf_counter()
405
+ times.append(end - start)
406
+ mag_out.append(mag_t)
407
+ pha_out.append(pha_t)
408
+
409
+ # 2. frame by frame inference
410
+ for t in range(args.look_ahead_frames+1, noisy_mag.shape[-1]):
411
+ start = time.perf_counter()
412
+ mag_t, pha_t = streaming_model(
413
+ noisy_mag[:, :, t:t+1],
414
+ noisy_pha[:, :, t:t+1],
415
+ args.Exit_layer,
416
+ args.look_ahead_frames
417
+ )
418
+ torch.cuda.synchronize()
419
+ end = time.perf_counter()
420
+
421
+ # To remove "strange sweep artifact"
422
+ mag = torch.expm1(RELU(mag_t)) # [1, F, 1]
423
+ zero_portion = torch.sum(mag==0, 1)/mag.shape[1]
424
+ if zero_portion.item()>0.5:
425
+ mag_t = 0*mag_t
426
+ times.append(end - start)
427
+ mag_out.append(mag_t)
428
+ pha_out.append(pha_t)
429
+
430
+ mag_out = torch.stack(mag_out, dim=-1)
431
+ pha_out = torch.stack(pha_out, dim=-1)
432
+
433
+ total_audio_time = noisy_wav.shape[-1] / noisy_sr
434
+
435
+ audio_g = mag_phase_istft(mag_out, pha_out, n_fft_scaled, hop_size_scaled, win_size_scaled, compress_factor)
436
+ audio_g = pad_or_trim_to_match(noisy_wav.detach(), audio_g, pad_value=1e-8) # Align lengths using epsilon padding
437
+ torchaudio.save('./enhanced_audio/online_enhanced_mic_test.flac', audio_g.cpu(), noisy_sr)
438
+ print(f"Max waveform difference of Online and Offline inference: {(audio_g2-audio_g).max():.7f}")
439
+ #import pdb; pdb.set_trace()
440
+ return noisy_sr, sum(times)/len(times)*1000, sum(times)/total_audio_time
441
+
442
+
443
+
444
+ def main():
445
+ print('Initializing Inference Process..')
446
+ parser = argparse.ArgumentParser()
447
+ parser.add_argument('--config', default='results')
448
+ parser.add_argument('--Exit_layer', type=int, required=True)
449
+ parser.add_argument('--look_ahead_frames', type=int, required=True)
450
+ parser.add_argument('--BWE', default=None)
451
+ args = parser.parse_args()
452
+
453
+ global device
454
+ if torch.cuda.is_available():
455
+ device = torch.device('cuda')
456
+ else:
457
+ raise RuntimeError("Currently, CPU mode is not supported.")
458
+
459
+ sr, latency_ms, rtf = inference(args, device)
460
+ #print(args.checkpoint_file)
461
+ print(f"Layer use: {args.Exit_layer:.0f}")
462
+ print(f"Look ahead frames: {args.look_ahead_frames:.0f}")
463
+ print(f"Sampling rate: {sr:.3f}")
464
+ print(f"Online Latency per chunk: {latency_ms:.3f} ms")
465
+ print(f"Online Real-Time Factor (RTF): {rtf:.3f}")
466
+
467
+
468
+ if __name__ == '__main__':
469
+ main()
470
+
online_inference.sh ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ pip install triton==2.3.0
2
+ CUDA_VISIBLE_DEVICES='0' python online_inference.py \
3
+ --config recipes/USEMamba_12x1_lr_00002_norm_05_vq_067_nfft_320_hop_160_NRIR_012_pha_0005_com_04_early_005_release_random_layer_GAN_longer_1k.yaml \
4
+ --Exit_layer 8 \
5
+ --look_ahead_frames 0 \
6
+ #--BWE 16000 \
7
+ # Exit_layer can be between 3~12 , look_ahead_frames can be between 0~2
recipes/USEMamba_12x1_lr_00002_norm_05_vq_067_nfft_320_hop_160_NRIR_012_pha_0005_com_04_early_005_release_random_layer_GAN_longer_1k.yaml ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Environment Settings
2
+ # These settings specify the hardware and distributed setup for the model training.
3
+ # Adjust `num_gpus` and `dist_config` according to your distributed training environment.
4
+ env_setting:
5
+ num_gpus: 8 # Number of GPUs. Now we don't support CPU mode.
6
+ num_workers: 20 # 0 Number of worker threads for data loading.
7
+ persistent_workers: True # False If you have large RAM, turn this to be True
8
+ prefetch_factor: 8 # null
9
+ seed: 1234 # Seed for random number generators to ensure reproducibility.
10
+ stdout_interval: 1000
11
+ checkpoint_interval: 1000 # save model to ckpt every N steps
12
+ validation_interval: 1000
13
+ dist_cfg:
14
+ dist_backend: nccl # Distributed training backend, 'nccl' for NVIDIA GPUs.
15
+ dist_url: tcp://localhost:19478 # URL for initializing distributed training.
16
+ world_size: 1 # Total number of processes in the distributed training.
17
+ pin_memory: True # If you have large RAM, turn this to be True
18
+
19
+ # STFT Configuration
20
+ # Configuration for Short-Time Fourier Transform (STFT), crucial for audio processing models.
21
+ stft_cfg:
22
+ sampling_rate: 8000 # Audio sampling rate in Hz.
23
+ n_fft: 320 # FFT components for transforming audio signals.
24
+ hop_size: 160 # Samples between successive frames.
25
+ win_size: 320 # Window size used in FFT.
26
+ sfi: True # Sampline Frequency Independent
27
+
28
+ # Model Configuration
29
+ # Defines the architecture specifics of the model, including layer configurations and feature compression.
30
+ model_cfg:
31
+ hid_feature: 64 # Channels in dense layers.
32
+ compress_factor: relu_log1p # Compression factor applied to extracted features.
33
+ num_tfmamba: 12 # Number of Time-Frequency Mamba (TFMamba) blocks in the model.
34
+ d_state: 16 # Dimensionality of the state vector in Mamba blocks.
35
+ d_conv: 4 # Convolutional layer dimensionality within Mamba blocks.
36
+ expand: 4 # Expansion factor for the layers within the Mamba blocks.
37
+ norm_epsilon: 0.00001 # Numerical stability in normalization layers within the Mamba blocks.
38
+ beta: 2.0 # Hyperparameter for the Learnable Sigmoid function.
39
+ input_channel: 2 # Magnitude and Phase
40
+ output_channel: 1 # Single Channel Speech Enhancement
41
+ inner_mamba_nlayer: 1 # Number of layer of Mamba in Bidirectional Mamba
42
+ nonlinear: None # last activation function for the mag encoder. 'softplus' or 'relu'
43
+ mapping: True # Otherwise, this should be masking model
44
+ maximum_layer: 12
utils/util.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
2
+ #
3
+ # NVIDIA CORPORATION and its licensors retain all intellectual property
4
+ # and proprietary rights in and to this software, related documentation
5
+ # and any modifications thereto. Any use, reproduction, disclosure or
6
+ # distribution of this software and related documentation without an express
7
+ # license agreement from NVIDIA CORPORATION is strictly prohibited.
8
+
9
+ import yaml
10
+ import torch
11
+ import os
12
+ import shutil
13
+ import torch.nn.functional as F
14
+
15
+ def load_config(config_path):
16
+ """Load configuration from a YAML file."""
17
+ with open(config_path, 'r') as file:
18
+ return yaml.safe_load(file)
19
+
20
+ def pad_or_trim_to_match(reference: torch.Tensor, target: torch.Tensor, pad_value: float = 1e-6) -> torch.Tensor:
21
+ """
22
+ Extends the target tensor to match the reference tensor along dim=1
23
+ without breaking autograd, by creating a new tensor and copying data in.
24
+ """
25
+ B, ref_len = reference.shape
26
+ _, tgt_len = target.shape
27
+
28
+ if tgt_len == ref_len:
29
+ return target
30
+ elif tgt_len > ref_len:
31
+ return target[:, :ref_len]
32
+
33
+ # Allocate padded tensor with grad support
34
+ padded = torch.full((B, ref_len), pad_value, dtype=target.dtype, device=target.device)
35
+ padded[:, :tgt_len] = target # This preserves gradient tracking
36
+
37
+ return padded