Audio-to-Audio
MambaSSM
Safetensors
universal speech enhancement
multiple input sampling rates
language-agnostic
Instructions to use Freightliner/RE-USE with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MambaSSM
How to use Freightliner/RE-USE with MambaSSM:
from mamba_ssm import MambaLMHeadModel model = MambaLMHeadModel.from_pretrained("Freightliner/RE-USE") - Notebooks
- Google Colab
- Kaggle
Commit ·
01f74f7
0
Parent(s):
Duplicate from nvidia/RE-USE
Browse filesCo-authored-by: Szu-Wei Fu <szuweifu@users.noreply.huggingface.co>
- .gitattributes +37 -0
- README.md +163 -0
- config.json +40 -0
- inference.py +113 -0
- inference.sh +6 -0
- inference_chunk.py +134 -0
- inference_chunk.sh +9 -0
- long_noisy_audio/mic_test2.wav +3 -0
- model.safetensors +3 -0
- models/codec_module_time_d4.py +168 -0
- models/generator_SEMamba_time_d4.py +92 -0
- models/mamba_block2_SEMamba.py +81 -0
- models/stfts.py +95 -0
- noisy_audio/mic_test2.wav +3 -0
- recipes/USEMamba_30x1_lr_00002_norm_05_vq_065_nfft_320_hop_40_NRIR_012_pha_0005_com_04_early_001.yaml +44 -0
- utils/util.py +37 -0
.gitattributes
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz 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 |
+
long_noisy_audio/mic_test2.wav filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
noisy_audio/mic_test2.wav filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: other
|
| 3 |
+
track_downloads: true
|
| 4 |
+
pipeline_tag: audio-to-audio
|
| 5 |
+
library_name: mamba-ssm
|
| 6 |
+
tags:
|
| 7 |
+
- universal speech enhancement
|
| 8 |
+
- multiple input sampling rates
|
| 9 |
+
- language-agnostic
|
| 10 |
+
---
|
| 11 |
+
# **<span style="color:#76b900;">🤫 RE-USE: Multilingual Universal Speech Enhancement</span>**
|
| 12 |
+
# Model Overview
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
## Description
|
| 16 |
+
In universal speech enhancement, the goal is to restore the **quality** of diverse degraded speech while preserving **fidelity**, ensuring that all other factors remain unchanged, e.g., linguistic content, speaker identity, emotion, accent, and other paralinguistic attributes. Inspired by the **distortion–perception trade-off theory**, our proposed single model achieves a good balance between these two objectives and has the following desirable properties:
|
| 17 |
+
|
| 18 |
+
- Robustness to **diverse degradations**, including additive noise, reverberation, clipping, bandwidth limitation, codec artifacts, packet loss and low-quality mics .
|
| 19 |
+
- Support for **multiple input sampling rates**, including 8, 16, 22.05, 24, 32, 44.1, and 48 kHz.
|
| 20 |
+
- Strong **language-agnostic** capability, enabling effective performance across different languages.
|
| 21 |
+
|
| 22 |
+
This model is for research and development only.
|
| 23 |
+
|
| 24 |
+
## Usage
|
| 25 |
+
Directly try our [**Gradio Interactive Demo**](https://huggingface.co/spaces/nvidia/RE-USE) by uploading your noisy audio/video !!
|
| 26 |
+
|
| 27 |
+
## Environment Setup
|
| 28 |
+
1. (For **Mamba** setup)Pre-built Docker environments can be downloaded [here](https://github.com/RoyChao19477/SEMamba?tab=readme-ov-file#-docker-support) to simplify **Mamba** setup.
|
| 29 |
+
|
| 30 |
+
2. If you need bandwidth extension:
|
| 31 |
+
|
| 32 |
+
```bash
|
| 33 |
+
pip install resampy
|
| 34 |
+
```
|
| 35 |
+
3. Download and navigate to the HuggingFace repository:
|
| 36 |
+
```
|
| 37 |
+
huggingface-cli download nvidia/RE-USE --local-dir ./REUSE --local-dir-use-symlinks False
|
| 38 |
+
cd ./REUSE
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
## Inference
|
| 42 |
+
Follow the simple steps below to generate enhanced speech using our model:
|
| 43 |
+
1. Place your noisy speech files in the folder `noisy_audio/`
|
| 44 |
+
2. Run the following command:
|
| 45 |
+
```bash
|
| 46 |
+
sh inference.sh
|
| 47 |
+
```
|
| 48 |
+
3. The enhanced speech files will be saved in `enhanced_audio/`.
|
| 49 |
+
|
| 50 |
+
That's all !
|
| 51 |
+
|
| 52 |
+
**Note:**
|
| 53 |
+
|
| 54 |
+
a. You can enable bandwidth extension by setting the target bandwidth using the `BWE argument` in the script.
|
| 55 |
+
|
| 56 |
+
---
|
| 57 |
+
|
| 58 |
+
If your noisy speech files are **long and may cause GPU out-of-memory (OOM)** errors, please use the following procedure instead:
|
| 59 |
+
1. Place your long noisy speech files in the folder `long_noisy_audio/`
|
| 60 |
+
2. Run the following command:
|
| 61 |
+
```bash
|
| 62 |
+
sh inference_chunk.sh
|
| 63 |
+
```
|
| 64 |
+
3. The enhanced speech files will be saved in `Long_enhanced_audio/`.
|
| 65 |
+
|
| 66 |
+
**Note:**
|
| 67 |
+
|
| 68 |
+
a. You can enable bandwidth extension by setting the target bandwidth using the `BWE argument` in the script.
|
| 69 |
+
|
| 70 |
+
b. You can also configure the `chunk_size_in_seconds` and `hop_length_portion` directly in the script.
|
| 71 |
+
|
| 72 |
+
---
|
| 73 |
+
|
| 74 |
+
## License/Terms of Use
|
| 75 |
+
This model is released under the [NVIDIA One-Way Noncommercial License (NSCLv1)](https://github.com/NVlabs/HMAR/blob/main/LICENSE).
|
| 76 |
+
|
| 77 |
+
## Deployment Geography
|
| 78 |
+
Global.
|
| 79 |
+
|
| 80 |
+
## Use Case
|
| 81 |
+
Researchers and general users can use this model to enhance the quality of their speech data.
|
| 82 |
+
|
| 83 |
+
## Release Date
|
| 84 |
+
Hugging Face 2026/03/18
|
| 85 |
+
|
| 86 |
+
## References
|
| 87 |
+
[1] [Rethinking Training Targets, Architectures and Data Quality for Universal Speech Enhancement](https://arxiv.org/abs/2603.02641), 2025.
|
| 88 |
+
(Note: The released model checkpoint differs from the one reported in the paper. It incorporates additional degradation types (e.g., microphone response and more codecs) and is fine-tuned on a smaller, high-quality clean subset.)
|
| 89 |
+
|
| 90 |
+
## Model Architecture
|
| 91 |
+
**Architecture Type:** Convolutional encoder, Convolutional decoder, and Mamba for time–frequency modeling <br>
|
| 92 |
+
**Network Architecture:** Bi-directional Mamba with 30 layers <br>
|
| 93 |
+
**Number of model parameters:** 9.6M <br>
|
| 94 |
+
|
| 95 |
+
## Input
|
| 96 |
+
Input Type(s): Audio <br>
|
| 97 |
+
Input Format(s): .wav files <br>
|
| 98 |
+
Input Parameters: One-Dimensional (1D) <br>
|
| 99 |
+
Other Properties Related to Input: 8000 Hz - 48000 Hz Mono-channel Audio <br>
|
| 100 |
+
|
| 101 |
+
## Output
|
| 102 |
+
Output Type(s): Audio <br>
|
| 103 |
+
Output Format: .wav files <br>
|
| 104 |
+
Output Parameters: One-Dimensional (1D) <br>
|
| 105 |
+
Other Properties Related to Output: 8000 Hz - 48000 Hz Mono-channel Audio <br>
|
| 106 |
+
|
| 107 |
+
Our AI models are designed and/or optimized to run on NVIDIA GPU-accelerated systems. By leveraging NVIDIA’s hardware (e.g. GPU cores) and software frameworks (e.g., CUDA libraries), the model achieves faster training and inference times compared to CPU-only solutions.
|
| 108 |
+
|
| 109 |
+
## Software Integration
|
| 110 |
+
**Runtime Engine(s):**
|
| 111 |
+
* Not Applicable (N/A)
|
| 112 |
+
|
| 113 |
+
**Supported Hardware Microarchitecture Compatibility:**
|
| 114 |
+
* NVIDIA Ampere (A100)
|
| 115 |
+
|
| 116 |
+
**Preferred Operating System(s):**
|
| 117 |
+
* Linux
|
| 118 |
+
|
| 119 |
+
The integration of foundation and fine-tuned models into AI systems requires additional testing using use-case-specific data to ensure safe and effective deployment. Following the V-model methodology, iterative testing and validation at both unit and system levels are essential to mitigate risks, meet technical and functional requirements, and ensure compliance with safety and ethical standards before deployment.
|
| 120 |
+
|
| 121 |
+
## Model Version(s)
|
| 122 |
+
Current version: 30USEMamba_peak+GAN_tel_mic_1134k
|
| 123 |
+
|
| 124 |
+
## Training Datasets
|
| 125 |
+
**Data Modality:**
|
| 126 |
+
Audio
|
| 127 |
+
|
| 128 |
+
**Audio Training Data Size:**
|
| 129 |
+
Less than 10,000 Hours
|
| 130 |
+
|
| 131 |
+
* [LibriVox data from DNS5 challenge (EN)](https://github.com/microsoft/DNS-Challenge/tree/master) (~350 hours of speech data)
|
| 132 |
+
* [LibriTTS (EN)](https://openslr.org/60/) (~200 hours of speech data)
|
| 133 |
+
* [VCTK (EN)](https://datashare.ed.ac.uk/handle/10283/3443) (~80 hours of speech data)
|
| 134 |
+
* [WSJ (EN)](https://catalog.ldc.upenn.edu/LDC93S6A) (~85 hours of speech data)
|
| 135 |
+
* [EARS (EN)](https://sp-uhh.github.io/ears_dataset/) (~100 hours of speech data)
|
| 136 |
+
* [Multilingual Librispeech (De, En, Es, Fr)](https://www.openslr.org/94/) (~450 hours of speech data)
|
| 137 |
+
* [CommonVoice 19.0 (De, En, Es, Fr, zh-CN)](https://huggingface.co/datasets/fsicoli/common_voice_19_0) (~1300 hours of speech data)
|
| 138 |
+
* [Audioset+FreeSound noise in DNS5 challenge](https://github.com/microsoft/DNS-Challenge/tree/master) (~180 hours of noise data)
|
| 139 |
+
* [WHAM! Noise](http://wham.whisper.ai/) (~80 hours of noise data)
|
| 140 |
+
* [FSD50K (human voice filtered)](https://huggingface.co/datasets/Fhrozen/FSD50k) (~100 hours of non-speech data)
|
| 141 |
+
* [(Part of) Free Music Archive (medium)](https://github.com/mdeff/fma) (~200 hours of non-speech data)
|
| 142 |
+
* [Simulated RIRs from DNS5 challenge](https://github.com/microsoft/DNS-Challenge/tree/master) (~60k samples of room impulse response)
|
| 143 |
+
* [MicIRP](https://micirp.blogspot.com/p/about-micirp.html) (~70 samples of microphone impulse response)
|
| 144 |
+
|
| 145 |
+
## Inference
|
| 146 |
+
**Acceleration Engine:** None <br>
|
| 147 |
+
**Test Hardware:** NVIDIA A100
|
| 148 |
+
|
| 149 |
+
## Ethical Considerations
|
| 150 |
+
NVIDIA believes Trustworthy AI is a shared responsibility and we have established policies and practices to enable development for a wide array of AI applications. When downloaded or used in accordance with our terms of service, developers should work with their internal model team to ensure this model meets requirements for the relevant industry and use case and addresses unforeseen product misuse.
|
| 151 |
+
Please report model quality, risk, security vulnerabilities or NVIDIA AI Concerns [here](https://app.intigriti.com/programs/nvidia/nvidiavdp/detail).
|
| 152 |
+
|
| 153 |
+
## Citation
|
| 154 |
+
Please consider to cite our paper and this framework, if they are helpful in your research.
|
| 155 |
+
|
| 156 |
+
```bibtex
|
| 157 |
+
@article{fu2026rethinking,
|
| 158 |
+
title={Rethinking Training Targets, Architectures and Data Quality for Universal Speech Enhancement},
|
| 159 |
+
author={Fu, Szu-Wei and Chao, Rong and Yang, Xuesong and Huang, Sung-Feng and Zezario, Ryandhimas E and Nasretdinov, Rauf and Juki{\'c}, Ante and Tsao, Yu and Wang, Yu-Chiang Frank},
|
| 160 |
+
journal={arXiv preprint arXiv:2603.02641},
|
| 161 |
+
year={2026}
|
| 162 |
+
}
|
| 163 |
+
```
|
config.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"env_setting": {
|
| 3 |
+
"checkpoint_interval": 5000,
|
| 4 |
+
"dist_cfg": {
|
| 5 |
+
"dist_backend": "nccl",
|
| 6 |
+
"dist_url": "tcp://localhost:19478",
|
| 7 |
+
"world_size": 1
|
| 8 |
+
},
|
| 9 |
+
"num_gpus": 8,
|
| 10 |
+
"num_workers": 20,
|
| 11 |
+
"persistent_workers": true,
|
| 12 |
+
"pin_memory": true,
|
| 13 |
+
"prefetch_factor": 8,
|
| 14 |
+
"seed": 1234,
|
| 15 |
+
"stdout_interval": 5000,
|
| 16 |
+
"validation_interval": 5000
|
| 17 |
+
},
|
| 18 |
+
"model_cfg": {
|
| 19 |
+
"beta": 2.0,
|
| 20 |
+
"compress_factor": "relu_log1p",
|
| 21 |
+
"d_conv": 4,
|
| 22 |
+
"d_state": 16,
|
| 23 |
+
"expand": 4,
|
| 24 |
+
"hid_feature": 64,
|
| 25 |
+
"inner_mamba_nlayer": 1,
|
| 26 |
+
"input_channel": 2,
|
| 27 |
+
"mapping": true,
|
| 28 |
+
"nonlinear": "None",
|
| 29 |
+
"norm_epsilon": 1e-05,
|
| 30 |
+
"num_tfmamba": 30,
|
| 31 |
+
"output_channel": 1
|
| 32 |
+
},
|
| 33 |
+
"stft_cfg": {
|
| 34 |
+
"hop_size": 40,
|
| 35 |
+
"n_fft": 320,
|
| 36 |
+
"sampling_rate": 8000,
|
| 37 |
+
"sfi": true,
|
| 38 |
+
"win_size": 320
|
| 39 |
+
}
|
| 40 |
+
}
|
inference.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.generator_SEMamba_time_d4 import SEMamba
|
| 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/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.from_pretrained("nvidia/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 |
+
amp_g, pha_g, _ = SE_model(noisy_mag, noisy_pha)
|
| 81 |
+
# To remove "strange sweep artifact"
|
| 82 |
+
mag = torch.expm1(RELU(amp_g)) # [1, F, T]
|
| 83 |
+
zero_portion = torch.sum(mag==0, 1)/mag.shape[1]
|
| 84 |
+
amp_g[:,:,(zero_portion>0.5)[0]] = 0
|
| 85 |
+
|
| 86 |
+
audio_g = mag_phase_istft(amp_g, pha_g, n_fft_scaled, hop_size_scaled, win_size_scaled, compress_factor)
|
| 87 |
+
audio_g = pad_or_trim_to_match(noisy_wav.detach(), audio_g, pad_value=1e-8) # Align lengths using epsilon padding
|
| 88 |
+
assert audio_g.shape == noisy_wav.shape, audio_g.shape
|
| 89 |
+
|
| 90 |
+
output_file = os.path.join(args.output_folder + fname.replace(args.input_folder,'').split('.')[0]+'.flac') # save to .flac format
|
| 91 |
+
torchaudio.save(output_file, audio_g.cpu(), noisy_sr)
|
| 92 |
+
|
| 93 |
+
def main():
|
| 94 |
+
print('Initializing Inference Process...')
|
| 95 |
+
parser = argparse.ArgumentParser()
|
| 96 |
+
parser.add_argument('--input_folder')
|
| 97 |
+
parser.add_argument('--output_folder')
|
| 98 |
+
parser.add_argument('--config')
|
| 99 |
+
parser.add_argument('--checkpoint_file', required=True)
|
| 100 |
+
parser.add_argument('--BWE', default=None)
|
| 101 |
+
args = parser.parse_args()
|
| 102 |
+
|
| 103 |
+
global device
|
| 104 |
+
if torch.cuda.is_available():
|
| 105 |
+
device = torch.device('cuda')
|
| 106 |
+
else:
|
| 107 |
+
raise RuntimeError("Currently, CPU mode is not supported.")
|
| 108 |
+
|
| 109 |
+
inference(args, device)
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
if __name__ == '__main__':
|
| 113 |
+
main()
|
inference.sh
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
CUDA_VISIBLE_DEVICES='0' python ./inference.py \
|
| 2 |
+
--input_folder ./noisy_audio \
|
| 3 |
+
--output_folder ./enhanced_audio \
|
| 4 |
+
--checkpoint_file ./exp/30x1_lr_00002_norm_05_vq_065_nfft_320_hop_40_NRIR_012_pha_0005_com_04_early_peak_GAN_tel_mic/g_01134000.pth \
|
| 5 |
+
--config ./recipes/USEMamba_30x1_lr_00002_norm_05_vq_065_nfft_320_hop_40_NRIR_012_pha_0005_com_04_early_001.yaml \
|
| 6 |
+
#--BWE 32000 \
|
inference_chunk.py
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
import math
|
| 16 |
+
from models.stfts import mag_phase_stft, mag_phase_istft
|
| 17 |
+
from models.generator_SEMamba_time_d4 import SEMamba
|
| 18 |
+
from utils.util import load_config, pad_or_trim_to_match
|
| 19 |
+
from huggingface_hub import hf_hub_download
|
| 20 |
+
|
| 21 |
+
RELU = nn.ReLU()
|
| 22 |
+
|
| 23 |
+
config_path = hf_hub_download(repo_id="nvidia/RE-USE", filename="config.json")
|
| 24 |
+
|
| 25 |
+
def get_filepaths(directory, file_type=None):
|
| 26 |
+
file_paths = [] # List which will store all of the full filepaths.
|
| 27 |
+
# Walk the tree.
|
| 28 |
+
for root, directories, files in os.walk(directory):
|
| 29 |
+
for filename in files:
|
| 30 |
+
# Join the two strings in order to form the full filepath.
|
| 31 |
+
filepath = os.path.join(root, filename)
|
| 32 |
+
if file_type is not None:
|
| 33 |
+
if filepath.split('.')[-1] == file_type:
|
| 34 |
+
file_paths.append(filepath) # Add it to the list.
|
| 35 |
+
else:
|
| 36 |
+
file_paths.append(filepath) # Add it to the list.
|
| 37 |
+
return file_paths # Self-explanatory.
|
| 38 |
+
|
| 39 |
+
def make_even(value):
|
| 40 |
+
value = int(round(value))
|
| 41 |
+
return value if value % 2 == 0 else value + 1
|
| 42 |
+
|
| 43 |
+
def inference(args, device):
|
| 44 |
+
cfg = load_config(args.config)
|
| 45 |
+
n_fft, hop_size, win_size = cfg['stft_cfg']['n_fft'], cfg['stft_cfg']['hop_size'], cfg['stft_cfg']['win_size']
|
| 46 |
+
compress_factor = cfg['model_cfg']['compress_factor']
|
| 47 |
+
sampling_rate = cfg['stft_cfg']['sampling_rate']
|
| 48 |
+
|
| 49 |
+
SE_model = SEMamba.from_pretrained("nvidia/RE-USE", cfg=cfg).to(device)
|
| 50 |
+
SE_model.eval()
|
| 51 |
+
|
| 52 |
+
os.makedirs(args.output_folder, exist_ok=True)
|
| 53 |
+
with torch.no_grad():
|
| 54 |
+
for fname in get_filepaths(args.input_folder):
|
| 55 |
+
print(fname)
|
| 56 |
+
try:
|
| 57 |
+
os.makedirs(args.output_folder + fname[0:fname.rfind('/')].replace(args.input_folder,''), exist_ok=True)
|
| 58 |
+
Noisy_wav, noisy_sr = torchaudio.load(fname)
|
| 59 |
+
except Exception as e:
|
| 60 |
+
print(f"Warning: cannot read {fname}, skipping. ({e})")
|
| 61 |
+
continue
|
| 62 |
+
|
| 63 |
+
if args.BWE is not None:
|
| 64 |
+
opts = {"res_type": "kaiser_best"}
|
| 65 |
+
Noisy_wav = librosa.resample(Noisy_wav.cpu().numpy(), orig_sr=noisy_sr, target_sr=int(args.BWE), **opts)
|
| 66 |
+
noisy_sr = int(args.BWE)
|
| 67 |
+
|
| 68 |
+
chunk_size = int(args.chunk_size_in_seconds*noisy_sr) # (in samples)
|
| 69 |
+
hop_length = int(args.hop_length_portion*chunk_size) # (in samples)
|
| 70 |
+
window = torch.hann_window(chunk_size).to(device)
|
| 71 |
+
|
| 72 |
+
n_fft_scaled = make_even(n_fft * noisy_sr // sampling_rate)
|
| 73 |
+
hop_size_scaled = make_even(hop_size * noisy_sr // sampling_rate)
|
| 74 |
+
win_size_scaled = make_even(win_size * noisy_sr // sampling_rate)
|
| 75 |
+
|
| 76 |
+
Noisy_wav = torch.FloatTensor(Noisy_wav).to(device)
|
| 77 |
+
audio_enhanced = torch.zeros_like(Noisy_wav).to(device)
|
| 78 |
+
#norm = torch.zeros_like(Noisy_wav).to(device)
|
| 79 |
+
window_sum = torch.zeros_like(Noisy_wav).to(device)
|
| 80 |
+
for c in range(Noisy_wav.shape[0]): # for multi-channel speech
|
| 81 |
+
noisy_wav = Noisy_wav[c:c+1,:]
|
| 82 |
+
for i in range(max(1, math.ceil((noisy_wav.shape[1]-chunk_size)/hop_length)+1)):
|
| 83 |
+
noisy_wav_chunk = noisy_wav[:, i*hop_length : i*hop_length+chunk_size]
|
| 84 |
+
|
| 85 |
+
noisy_mag, noisy_pha, noisy_com = mag_phase_stft(
|
| 86 |
+
noisy_wav_chunk,
|
| 87 |
+
n_fft=n_fft_scaled,
|
| 88 |
+
hop_size=hop_size_scaled,
|
| 89 |
+
win_size=win_size_scaled,
|
| 90 |
+
compress_factor=compress_factor,
|
| 91 |
+
center=True,
|
| 92 |
+
addeps=False
|
| 93 |
+
)
|
| 94 |
+
amp_g, pha_g, _ = SE_model(noisy_mag, noisy_pha)
|
| 95 |
+
# To remove "strange sweep artifact"
|
| 96 |
+
mag = torch.expm1(RELU(amp_g)) # [1, F, T]
|
| 97 |
+
zero_portion = torch.sum(mag==0, 1)/mag.shape[1]
|
| 98 |
+
amp_g[:,:,(zero_portion>0.5)[0]] = 0
|
| 99 |
+
|
| 100 |
+
audio_g = mag_phase_istft(amp_g, pha_g, n_fft_scaled, hop_size_scaled, win_size_scaled, compress_factor)
|
| 101 |
+
audio_g = pad_or_trim_to_match(noisy_wav_chunk.detach(), audio_g, pad_value=1e-8) # Align lengths using epsilon padding
|
| 102 |
+
|
| 103 |
+
audio_enhanced[c:c+1,i*hop_length:i*hop_length+chunk_size] += audio_g*window[0:audio_g.shape[1]]
|
| 104 |
+
window_sum[c:c+1,i*hop_length:i*hop_length+chunk_size] += window[0:audio_g.shape[1]]
|
| 105 |
+
#norm[c:c+1,i*hop_length:i*hop_length+chunk_size] += 1.0
|
| 106 |
+
nonzero_indices = (window_sum > 1e-8)
|
| 107 |
+
audio_enhanced[:,nonzero_indices[0]] = audio_enhanced[:,nonzero_indices[0]]/window_sum[:,nonzero_indices[0]]
|
| 108 |
+
assert audio_enhanced.shape == Noisy_wav.shape, audio_enhanced.shape
|
| 109 |
+
output_file = os.path.join(args.output_folder + fname.replace(args.input_folder,'').split('.')[0]+'.flac') # save to .flac format
|
| 110 |
+
torchaudio.save(output_file, audio_enhanced.cpu(), noisy_sr)
|
| 111 |
+
|
| 112 |
+
def main():
|
| 113 |
+
print('Initializing Inference Process..')
|
| 114 |
+
parser = argparse.ArgumentParser()
|
| 115 |
+
parser.add_argument('--input_folder')
|
| 116 |
+
parser.add_argument('--output_folder')
|
| 117 |
+
parser.add_argument('--config')
|
| 118 |
+
parser.add_argument('--checkpoint_file')
|
| 119 |
+
parser.add_argument('--chunk_size_in_seconds', type=float)
|
| 120 |
+
parser.add_argument('--hop_length_portion', type=float)
|
| 121 |
+
parser.add_argument('--BWE', default=None)
|
| 122 |
+
args = parser.parse_args()
|
| 123 |
+
|
| 124 |
+
global device
|
| 125 |
+
if torch.cuda.is_available():
|
| 126 |
+
device = torch.device('cuda')
|
| 127 |
+
else:
|
| 128 |
+
raise RuntimeError("Currently, CPU mode is not supported.")
|
| 129 |
+
|
| 130 |
+
inference(args, device)
|
| 131 |
+
|
| 132 |
+
if __name__ == '__main__':
|
| 133 |
+
main()
|
| 134 |
+
|
inference_chunk.sh
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
CUDA_VISIBLE_DEVICES='0' python ./inference_chunk.py \
|
| 2 |
+
--input_folder ./long_noisy_audio \
|
| 3 |
+
--output_folder ./long_enhanced_audio \
|
| 4 |
+
--checkpoint_file ./exp/30x1_lr_00002_norm_05_vq_065_nfft_320_hop_40_NRIR_012_pha_0005_com_04_early_peak_GAN_tel_mic/g_01134000.pth \
|
| 5 |
+
--config ./recipes/USEMamba_30x1_lr_00002_norm_05_vq_065_nfft_320_hop_40_NRIR_012_pha_0005_com_04_early_001.yaml \
|
| 6 |
+
--chunk_size_in_seconds 5\
|
| 7 |
+
--hop_length_portion 0.5\
|
| 8 |
+
#--BWE 32000 \
|
| 9 |
+
|
long_noisy_audio/mic_test2.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e6433420a00163f30a339fc83a191517a82597cf2141cbd310233470e83ce0d7
|
| 3 |
+
size 449896
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:87a4a970ce9aa79d5d92e71899ab034defcf13d93e5e4393ec0dc7db6d4ec048
|
| 3 |
+
size 38592940
|
models/codec_module_time_d4.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
|
| 14 |
+
def get_padding_2d(kernel_size, dilation=(1, 1)):
|
| 15 |
+
"""
|
| 16 |
+
Calculate the padding size for a 2D convolutional layer.
|
| 17 |
+
|
| 18 |
+
Args:
|
| 19 |
+
- kernel_size (tuple): Size of the convolutional kernel (height, width).
|
| 20 |
+
- dilation (tuple, optional): Dilation rate of the convolution (height, width). Defaults to (1, 1).
|
| 21 |
+
|
| 22 |
+
Returns:
|
| 23 |
+
- tuple: Calculated padding size (height, width).
|
| 24 |
+
"""
|
| 25 |
+
return (int((kernel_size[0] * dilation[0] - dilation[0]) / 2),
|
| 26 |
+
int((kernel_size[1] * dilation[1] - dilation[1]) / 2))
|
| 27 |
+
|
| 28 |
+
class SPConvTranspose2d(nn.Module):
|
| 29 |
+
def __init__(self, in_channels, out_channels, kernel_size, r=1):
|
| 30 |
+
super(SPConvTranspose2d, self).__init__()
|
| 31 |
+
self.pad1 = nn.ConstantPad2d((1, 1, 0, 0), value=0.)
|
| 32 |
+
self.out_channels = out_channels
|
| 33 |
+
self.conv = nn.Conv2d(in_channels, out_channels * r, kernel_size=kernel_size, stride=(1, 1))
|
| 34 |
+
self.r = r
|
| 35 |
+
|
| 36 |
+
def forward(self, x):
|
| 37 |
+
x = self.pad1(x)
|
| 38 |
+
out = self.conv(x)
|
| 39 |
+
batch_size, nchannels, H, W = out.shape
|
| 40 |
+
out = out.view((batch_size, self.r, nchannels // self.r, H, W))
|
| 41 |
+
out = out.permute(0, 2, 3, 4, 1)
|
| 42 |
+
out = out.contiguous().view((batch_size, nchannels // self.r, H, -1))
|
| 43 |
+
return out
|
| 44 |
+
|
| 45 |
+
class DenseBlock(nn.Module):
|
| 46 |
+
"""
|
| 47 |
+
DenseBlock module consisting of multiple convolutional layers with dilation.
|
| 48 |
+
"""
|
| 49 |
+
def __init__(self, cfg, kernel_size=(3, 3), depth=4):
|
| 50 |
+
super(DenseBlock, self).__init__()
|
| 51 |
+
self.cfg = cfg
|
| 52 |
+
self.depth = depth
|
| 53 |
+
self.dense_block = nn.ModuleList()
|
| 54 |
+
self.hid_feature = cfg['model_cfg']['hid_feature']
|
| 55 |
+
|
| 56 |
+
for i in range(depth):
|
| 57 |
+
dil = 2 ** i
|
| 58 |
+
dense_conv = nn.Sequential(
|
| 59 |
+
nn.Conv2d(self.hid_feature * (i + 1), self.hid_feature, kernel_size,
|
| 60 |
+
dilation=(dil, 1), padding=get_padding_2d(kernel_size, (dil, 1))),
|
| 61 |
+
nn.InstanceNorm2d(self.hid_feature, affine=True),
|
| 62 |
+
nn.PReLU(self.hid_feature)
|
| 63 |
+
)
|
| 64 |
+
self.dense_block.append(dense_conv)
|
| 65 |
+
|
| 66 |
+
def forward(self, x):
|
| 67 |
+
skip = x
|
| 68 |
+
for i in range(self.depth):
|
| 69 |
+
x = self.dense_block[i](skip)
|
| 70 |
+
skip = torch.cat([x, skip], dim=1)
|
| 71 |
+
return x
|
| 72 |
+
|
| 73 |
+
class DenseEncoder(nn.Module):
|
| 74 |
+
"""
|
| 75 |
+
DenseEncoder module consisting of initial convolution, dense block, and a final convolution.
|
| 76 |
+
"""
|
| 77 |
+
def __init__(self, cfg):
|
| 78 |
+
super(DenseEncoder, self).__init__()
|
| 79 |
+
self.cfg = cfg
|
| 80 |
+
self.input_channel = cfg['model_cfg']['input_channel']
|
| 81 |
+
self.hid_feature = cfg['model_cfg']['hid_feature']
|
| 82 |
+
|
| 83 |
+
self.dense_conv_1 = nn.Sequential(
|
| 84 |
+
nn.Conv2d(self.input_channel, self.hid_feature, (1, 1)),
|
| 85 |
+
nn.InstanceNorm2d(self.hid_feature, affine=True),
|
| 86 |
+
nn.PReLU(self.hid_feature)
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
self.dense_block = DenseBlock(cfg, depth=4)
|
| 90 |
+
|
| 91 |
+
self.dense_conv_2 = nn.Sequential(
|
| 92 |
+
nn.Conv2d(self.hid_feature, self.hid_feature, (1, 3), stride=(4, 2)),
|
| 93 |
+
nn.InstanceNorm2d(self.hid_feature, affine=True),
|
| 94 |
+
nn.PReLU(self.hid_feature)
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
def forward(self, x):
|
| 98 |
+
x = self.dense_conv_1(x) # [batch, hid_feature, time, freq]
|
| 99 |
+
x = self.dense_block(x) # [batch, hid_feature, time, freq]
|
| 100 |
+
x = self.dense_conv_2(x) # [batch, hid_feature, time, freq//2]
|
| 101 |
+
return x
|
| 102 |
+
|
| 103 |
+
class MagDecoder(nn.Module):
|
| 104 |
+
"""
|
| 105 |
+
MagDecoder module for decoding magnitude information.
|
| 106 |
+
"""
|
| 107 |
+
def __init__(self, cfg):
|
| 108 |
+
super(MagDecoder, self).__init__()
|
| 109 |
+
self.dense_block = DenseBlock(cfg, depth=4)
|
| 110 |
+
self.hid_feature = cfg['model_cfg']['hid_feature']
|
| 111 |
+
self.output_channel = cfg['model_cfg']['output_channel']
|
| 112 |
+
self.n_fft = cfg['stft_cfg']['n_fft']
|
| 113 |
+
self.beta = cfg['model_cfg']['beta']
|
| 114 |
+
|
| 115 |
+
self.up_conv1 = nn.Sequential(
|
| 116 |
+
SPConvTranspose2d(self.hid_feature, self.hid_feature, (1, 3), 2),
|
| 117 |
+
nn.InstanceNorm2d(self.hid_feature, affine=True),
|
| 118 |
+
nn.PReLU(self.hid_feature)
|
| 119 |
+
)
|
| 120 |
+
|
| 121 |
+
self.up_conv2 = nn.Sequential(
|
| 122 |
+
SPConvTranspose2d(self.hid_feature, self.hid_feature, (1, 3), 4),
|
| 123 |
+
nn.InstanceNorm2d(self.hid_feature, affine=True),
|
| 124 |
+
nn.PReLU(self.hid_feature)
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
self.final_conv = nn.Conv2d(self.hid_feature, self.output_channel, (1, 1))
|
| 128 |
+
|
| 129 |
+
def forward(self, x):
|
| 130 |
+
x = self.dense_block(x)
|
| 131 |
+
x = self.up_conv1(x)
|
| 132 |
+
x = self.up_conv2(x.permute(0,1,3,2)).permute(0,1,3,2)
|
| 133 |
+
x = self.final_conv(x)
|
| 134 |
+
return x
|
| 135 |
+
|
| 136 |
+
class PhaseDecoder(nn.Module):
|
| 137 |
+
"""
|
| 138 |
+
PhaseDecoder module for decoding phase information.
|
| 139 |
+
"""
|
| 140 |
+
def __init__(self, cfg):
|
| 141 |
+
super(PhaseDecoder, self).__init__()
|
| 142 |
+
self.dense_block = DenseBlock(cfg, depth=4)
|
| 143 |
+
self.hid_feature = cfg['model_cfg']['hid_feature']
|
| 144 |
+
self.output_channel = cfg['model_cfg']['output_channel']
|
| 145 |
+
|
| 146 |
+
self.up_conv1 = nn.Sequential(
|
| 147 |
+
SPConvTranspose2d(self.hid_feature, self.hid_feature, (1, 3), 2),
|
| 148 |
+
nn.InstanceNorm2d(self.hid_feature, affine=True),
|
| 149 |
+
nn.PReLU(self.hid_feature)
|
| 150 |
+
)
|
| 151 |
+
|
| 152 |
+
self.up_conv2 = nn.Sequential(
|
| 153 |
+
SPConvTranspose2d(self.hid_feature, self.hid_feature, (1, 3), 4),
|
| 154 |
+
nn.InstanceNorm2d(self.hid_feature, affine=True),
|
| 155 |
+
nn.PReLU(self.hid_feature)
|
| 156 |
+
)
|
| 157 |
+
|
| 158 |
+
self.phase_conv_r = nn.Conv2d(self.hid_feature, self.output_channel, (1, 1))
|
| 159 |
+
self.phase_conv_i = nn.Conv2d(self.hid_feature, self.output_channel, (1, 1))
|
| 160 |
+
|
| 161 |
+
def forward(self, x):
|
| 162 |
+
x = self.dense_block(x)
|
| 163 |
+
x = self.up_conv1(x)
|
| 164 |
+
x = self.up_conv2(x.permute(0,1,3,2)).permute(0,1,3,2)
|
| 165 |
+
x_r = self.phase_conv_r(x)
|
| 166 |
+
x_i = self.phase_conv_i(x)
|
| 167 |
+
x = torch.atan2(x_i, x_r)
|
| 168 |
+
return x
|
models/generator_SEMamba_time_d4.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 .mamba_block2_SEMamba import TFMambaBlock
|
| 14 |
+
from .codec_module_time_d4 import DenseEncoder, MagDecoder, PhaseDecoder
|
| 15 |
+
|
| 16 |
+
class SEMamba(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, 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 |
+
|
| 34 |
+
# Initialize dense encoder
|
| 35 |
+
self.dense_encoder = DenseEncoder(cfg)
|
| 36 |
+
|
| 37 |
+
# Initialize Mamba blocks
|
| 38 |
+
self.TSMamba = nn.ModuleList([TFMambaBlock(cfg) for _ in range(self.num_tscblocks)])
|
| 39 |
+
|
| 40 |
+
# Initialize decoders
|
| 41 |
+
self.mask_decoder = MagDecoder(cfg)
|
| 42 |
+
self.phase_decoder = PhaseDecoder(cfg)
|
| 43 |
+
|
| 44 |
+
def forward(self, noisy_mag, noisy_pha):
|
| 45 |
+
"""
|
| 46 |
+
Forward pass for the SEMamba model.
|
| 47 |
+
|
| 48 |
+
Args:
|
| 49 |
+
- noisy_mag (torch.Tensor): Noisy magnitude input tensor [B, F, T].
|
| 50 |
+
- noisy_pha (torch.Tensor): Noisy phase input tensor [B, F, T].
|
| 51 |
+
|
| 52 |
+
Returns:
|
| 53 |
+
- denoised_mag (torch.Tensor): Denoised magnitude tensor [B, F, T].
|
| 54 |
+
- denoised_pha (torch.Tensor): Denoised phase tensor [B, F, T].
|
| 55 |
+
- denoised_com (torch.Tensor): Denoised complex tensor [B, F, T, 2].
|
| 56 |
+
"""
|
| 57 |
+
# Reshape inputs
|
| 58 |
+
noisy_mag = rearrange(noisy_mag, 'b f t -> b t f').unsqueeze(1) # [B, 1, T, F]
|
| 59 |
+
noisy_pha = rearrange(noisy_pha, 'b f t -> b t f').unsqueeze(1) # [B, 1, T, F]
|
| 60 |
+
|
| 61 |
+
# Concatenate magnitude and phase inputs
|
| 62 |
+
x = torch.cat((noisy_mag, noisy_pha), dim=1) # [B, 2, T, F]
|
| 63 |
+
|
| 64 |
+
# Prevent unpredictable errors
|
| 65 |
+
B, C, T, F = x.shape
|
| 66 |
+
zeros = torch.zeros(B, C, T, 2, device=x.device)
|
| 67 |
+
x = torch.cat((x, zeros), dim=-1)
|
| 68 |
+
zeros = torch.zeros(B, C, 2, F+2, device=x.device)
|
| 69 |
+
x = torch.cat((x, zeros), dim=-2)
|
| 70 |
+
|
| 71 |
+
# Encode input
|
| 72 |
+
x = self.dense_encoder(x)
|
| 73 |
+
|
| 74 |
+
# Apply Mamba blocks
|
| 75 |
+
for block in self.TSMamba:
|
| 76 |
+
x = block(x)
|
| 77 |
+
|
| 78 |
+
# Decode output
|
| 79 |
+
denoised_mag = rearrange(self.mask_decoder(x), 'b c t f -> b f t c').squeeze(-1)
|
| 80 |
+
denoised_pha = rearrange(self.phase_decoder(x), 'b c t f -> b f t c').squeeze(-1)
|
| 81 |
+
|
| 82 |
+
# Prevent unpredictable errors
|
| 83 |
+
denoised_mag = denoised_mag[:, :F, :T]
|
| 84 |
+
denoised_pha = denoised_pha[:, :F, :T]
|
| 85 |
+
|
| 86 |
+
# Combine denoised magnitude and phase into a complex representation
|
| 87 |
+
denoised_com = torch.stack(
|
| 88 |
+
(denoised_mag * torch.cos(denoised_pha), denoised_mag * torch.sin(denoised_pha)),
|
| 89 |
+
dim=-1
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
return denoised_mag, denoised_pha, denoised_com
|
models/mamba_block2_SEMamba.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 MambaBlock(nn.Module):
|
| 19 |
+
def __init__(self, d_model, cfg):
|
| 20 |
+
super(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.backward_blocks = Mamba(d_model=d_model, d_state=d_state, d_conv=d_conv, expand=expand)
|
| 28 |
+
self.output_proj = nn.Linear(2 * d_model, d_model)
|
| 29 |
+
self.norm = nn.LayerNorm(d_model)
|
| 30 |
+
|
| 31 |
+
def forward(self, x):
|
| 32 |
+
# x: [B, T, D]
|
| 33 |
+
out_fw = self.forward_blocks(x) + x
|
| 34 |
+
|
| 35 |
+
out_bw = self.backward_blocks(torch.flip(x, dims=[1])) + torch.flip(x, dims=[1])
|
| 36 |
+
out_bw = torch.flip(out_bw, dims=[1])
|
| 37 |
+
|
| 38 |
+
out = torch.cat([out_fw, out_bw], dim=-1)
|
| 39 |
+
out = self.output_proj(out)
|
| 40 |
+
|
| 41 |
+
# LayerNorm
|
| 42 |
+
return self.norm(out)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class TFMambaBlock(nn.Module):
|
| 46 |
+
"""
|
| 47 |
+
Temporal-Frequency Mamba block for sequence modeling.
|
| 48 |
+
|
| 49 |
+
Attributes:
|
| 50 |
+
cfg (Config): Configuration for the block.
|
| 51 |
+
time_mamba (MambaBlock): Mamba block for temporal dimension.
|
| 52 |
+
freq_mamba (MambaBlock): Mamba block for frequency dimension.
|
| 53 |
+
tlinear (ConvTranspose1d): ConvTranspose1d layer for temporal dimension.
|
| 54 |
+
flinear (ConvTranspose1d): ConvTranspose1d layer for frequency dimension.
|
| 55 |
+
"""
|
| 56 |
+
def __init__(self, cfg):
|
| 57 |
+
super(TFMambaBlock, self).__init__()
|
| 58 |
+
self.cfg = cfg
|
| 59 |
+
self.hid_feature = cfg['model_cfg']['hid_feature']
|
| 60 |
+
|
| 61 |
+
# Initialize Mamba blocks
|
| 62 |
+
self.time_mamba = MambaBlock(d_model=self.hid_feature, cfg=cfg)
|
| 63 |
+
self.freq_mamba = MambaBlock(d_model=self.hid_feature, cfg=cfg)
|
| 64 |
+
|
| 65 |
+
def forward(self, x):
|
| 66 |
+
"""
|
| 67 |
+
Forward pass of the TFMamba block.
|
| 68 |
+
|
| 69 |
+
Parameters:
|
| 70 |
+
x (Tensor): Input tensor with shape (batch, channels, time, freq).
|
| 71 |
+
|
| 72 |
+
Returns:
|
| 73 |
+
Tensor: Output tensor after applying temporal and frequency Mamba blocks.
|
| 74 |
+
"""
|
| 75 |
+
b, c, t, f = x.size()
|
| 76 |
+
x = x.permute(0, 3, 2, 1).contiguous().view(b*f, t, c)
|
| 77 |
+
x = self.time_mamba(x) + x
|
| 78 |
+
x = x.view(b, f, t, c).permute(0, 2, 1, 3).contiguous().view(b*t, f, c)
|
| 79 |
+
x = self.freq_mamba(x) + x
|
| 80 |
+
x = x.view(b, t, f, c).permute(0, 3, 1, 2)
|
| 81 |
+
return x
|
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
|
noisy_audio/mic_test2.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e6433420a00163f30a339fc83a191517a82597cf2141cbd310233470e83ce0d7
|
| 3 |
+
size 449896
|
recipes/USEMamba_30x1_lr_00002_norm_05_vq_065_nfft_320_hop_40_NRIR_012_pha_0005_com_04_early_001.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: 5000
|
| 11 |
+
checkpoint_interval: 5000 # save model to ckpt every N steps
|
| 12 |
+
validation_interval: 5000
|
| 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 |
+
|
| 20 |
+
# STFT Configuration
|
| 21 |
+
# Configuration for Short-Time Fourier Transform (STFT), crucial for audio processing models.
|
| 22 |
+
stft_cfg:
|
| 23 |
+
sampling_rate: 8000 # Audio sampling rate in Hz.
|
| 24 |
+
n_fft: 320 # FFT components for transforming audio signals.
|
| 25 |
+
hop_size: 40 # Samples between successive frames.
|
| 26 |
+
win_size: 320 # Window size used in FFT.
|
| 27 |
+
sfi: True # Sampline Frequency Independent
|
| 28 |
+
|
| 29 |
+
# Model Configuration
|
| 30 |
+
# Defines the architecture specifics of the model, including layer configurations and feature compression.
|
| 31 |
+
model_cfg:
|
| 32 |
+
hid_feature: 64 # Channels in dense layers.
|
| 33 |
+
compress_factor: relu_log1p # Compression factor applied to extracted features.
|
| 34 |
+
num_tfmamba: 30 # Number of Time-Frequency Mamba (TFMamba) blocks in the model.
|
| 35 |
+
d_state: 16 # Dimensionality of the state vector in Mamba blocks.
|
| 36 |
+
d_conv: 4 # Convolutional layer dimensionality within Mamba blocks.
|
| 37 |
+
expand: 4 # Expansion factor for the layers within the Mamba blocks.
|
| 38 |
+
norm_epsilon: 0.00001 # Numerical stability in normalization layers within the Mamba blocks.
|
| 39 |
+
beta: 2.0 # Hyperparameter for the Learnable Sigmoid function.
|
| 40 |
+
input_channel: 2 # Magnitude and Phase
|
| 41 |
+
output_channel: 1 # Single Channel Speech Enhancement
|
| 42 |
+
inner_mamba_nlayer: 1 # Number of layer of Mamba in Bidirectional Mamba
|
| 43 |
+
nonlinear: None # last activation function for the mag encoder. 'softplus' or 'relu'
|
| 44 |
+
mapping: True # Otherwise, this should be masking model
|
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
|