Spaces:
Running
Running
Move to torch 2.8.0, load in the worker from a Hub mirror, and keep a lossless container
Browse filesMoves from torch 2.2.2, which ZeroGPU rejects, to 2.8.0, whose torchaudio still provides the module deepfilternet imports, loads the model in the worker alone from teamup-tech/DeepFilterNet-weights rather than the main branch of its GitHub repository, refuses an empty file, and returns a FLAC or AIFF input in its own container.
- app.py +26 -14
- requirements.txt +2 -2
app.py
CHANGED
|
@@ -2,13 +2,14 @@ import spaces # imported first, as ZeroGPU requires
|
|
| 2 |
|
| 3 |
import os
|
| 4 |
import tempfile
|
| 5 |
-
from pathlib import Path
|
| 6 |
|
| 7 |
import gradio as gr
|
| 8 |
import numpy as np
|
| 9 |
import soundfile as sf
|
| 10 |
import librosa
|
| 11 |
import torch
|
|
|
|
| 12 |
|
| 13 |
from pyharp import ModelCard, build_endpoint, SampleRate, Channels
|
| 14 |
from pyharp.tags import Subcategory
|
|
@@ -21,9 +22,8 @@ model_card = ModelCard(
|
|
| 21 |
name="DeepFilterNet",
|
| 22 |
description=(
|
| 23 |
"Background noise suppression / speech enhancement using "
|
| 24 |
-
"DeepFilterNet3.
|
| 25 |
-
"
|
| 26 |
-
"result is resampled back to the input's rate and copied across its "
|
| 27 |
"channels, so the output matches the format it came in with but carries "
|
| 28 |
"no stereo image. Repository: https://github.com/Rikorose/DeepFilterNet "
|
| 29 |
"Paper: https://arxiv.org/abs/2305.08227"
|
|
@@ -32,6 +32,10 @@ model_card = ModelCard(
|
|
| 32 |
tags=[Subcategory.DENOISING, SampleRate(48000), Channels(1)],
|
| 33 |
)
|
| 34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
# -----------------------------
|
| 36 |
# Device & Model Initialization
|
| 37 |
# -----------------------------
|
|
@@ -39,9 +43,18 @@ model_card = ModelCard(
|
|
| 39 |
DEVICE_STR = "cuda" if torch.cuda.is_available() else "cpu"
|
| 40 |
DEVICE = torch.device(DEVICE_STR)
|
| 41 |
|
| 42 |
-
#
|
| 43 |
-
|
| 44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
|
| 47 |
def load_audio_mono_48k(path: str):
|
|
@@ -91,9 +104,11 @@ def process_fn(input_audio_path: str, noise_atten_db: float) -> str:
|
|
| 91 |
if not input_audio_path:
|
| 92 |
raise gr.Error("Provide the speech to denoise.")
|
| 93 |
try:
|
| 94 |
-
sf.info(input_audio_path)
|
| 95 |
except Exception as exc:
|
| 96 |
raise gr.Error(f"Could not read the audio file: {exc}") from exc
|
|
|
|
|
|
|
| 97 |
|
| 98 |
return denoise(input_audio_path, noise_atten_db)
|
| 99 |
|
|
@@ -120,11 +135,6 @@ def denoise(input_audio_path: str, noise_atten_db: float) -> str:
|
|
| 120 |
# Slider strength
|
| 121 |
out = apply_attenuation_db(noisy, enhanced, noise_atten_db, max_db=30.0)
|
| 122 |
|
| 123 |
-
# Save output WAV and return path
|
| 124 |
-
out_dir = Path(tempfile.gettempdir()) / "pyharp_dfnet_outputs"
|
| 125 |
-
out_dir.mkdir(parents=True, exist_ok=True)
|
| 126 |
-
out_path = out_dir / "denoised.wav"
|
| 127 |
-
|
| 128 |
if original_sr != sr:
|
| 129 |
out = librosa.resample(out, orig_sr=sr, target_sr=original_sr)
|
| 130 |
|
|
@@ -133,6 +143,8 @@ def denoise(input_audio_path: str, noise_atten_db: float) -> str:
|
|
| 133 |
if original_channels > 1:
|
| 134 |
out = np.tile(out[:, None], (1, original_channels))
|
| 135 |
|
|
|
|
|
|
|
| 136 |
sf.write(str(out_path), out, original_sr)
|
| 137 |
return str(out_path)
|
| 138 |
|
|
@@ -169,4 +181,4 @@ if __name__ == "__main__":
|
|
| 169 |
process_fn=process_fn,
|
| 170 |
)
|
| 171 |
|
| 172 |
-
demo.queue().launch(show_error=True, pwa=True)
|
|
|
|
| 2 |
|
| 3 |
import os
|
| 4 |
import tempfile
|
| 5 |
+
from pathlib import Path
|
| 6 |
|
| 7 |
import gradio as gr
|
| 8 |
import numpy as np
|
| 9 |
import soundfile as sf
|
| 10 |
import librosa
|
| 11 |
import torch
|
| 12 |
+
from huggingface_hub import snapshot_download
|
| 13 |
|
| 14 |
from pyharp import ModelCard, build_endpoint, SampleRate, Channels
|
| 15 |
from pyharp.tags import Subcategory
|
|
|
|
| 22 |
name="DeepFilterNet",
|
| 23 |
description=(
|
| 24 |
"Background noise suppression / speech enhancement using "
|
| 25 |
+
"DeepFilterNet3. Slider controls strength through wet/dry blend. Denoising runs at "
|
| 26 |
+
"48 kHz in mono; the result is resampled back to the input's rate and copied across its "
|
|
|
|
| 27 |
"channels, so the output matches the format it came in with but carries "
|
| 28 |
"no stereo image. Repository: https://github.com/Rikorose/DeepFilterNet "
|
| 29 |
"Paper: https://arxiv.org/abs/2305.08227"
|
|
|
|
| 32 |
tags=[Subcategory.DENOISING, SampleRate(48000), Channels(1)],
|
| 33 |
)
|
| 34 |
|
| 35 |
+
# A lossless input keeps its container. A lossy one comes back as WAV, since encoding the result
|
| 36 |
+
# lossily again would expose the artifacts of the first encode and add a second set.
|
| 37 |
+
LOSSLESS = {".wav", ".flac", ".aiff", ".aif"}
|
| 38 |
+
|
| 39 |
# -----------------------------
|
| 40 |
# Device & Model Initialization
|
| 41 |
# -----------------------------
|
|
|
|
| 43 |
DEVICE_STR = "cuda" if torch.cuda.is_available() else "cpu"
|
| 44 |
DEVICE = torch.device(DEVICE_STR)
|
| 45 |
|
| 46 |
+
# pyharp runs process_fn in a worker, which imports this file as __mp_main__ and starts with the
|
| 47 |
+
# server, so the model is loaded there alone. deepfilternet would download it from the main branch
|
| 48 |
+
# of its GitHub repository, so it is read from a pinned mirror on the Hub instead, into a directory
|
| 49 |
+
# of its own, since loading writes beside the configuration.
|
| 50 |
+
if __name__ != "__main__":
|
| 51 |
+
_weights = snapshot_download(
|
| 52 |
+
"teamup-tech/DeepFilterNet-weights", revision="b2ec0e0c85db91c696d699a5cfc43ae7a3e3136a",
|
| 53 |
+
local_dir=os.path.join(tempfile.gettempdir(), "deepfilternet-weights"),
|
| 54 |
+
)
|
| 55 |
+
MODEL, DF_STATE, _ = init_df(model_base_dir=os.path.join(_weights, "DeepFilterNet3"),
|
| 56 |
+
log_file=None)
|
| 57 |
+
MODEL = MODEL.to(DEVICE)
|
| 58 |
|
| 59 |
|
| 60 |
def load_audio_mono_48k(path: str):
|
|
|
|
| 104 |
if not input_audio_path:
|
| 105 |
raise gr.Error("Provide the speech to denoise.")
|
| 106 |
try:
|
| 107 |
+
info = sf.info(input_audio_path)
|
| 108 |
except Exception as exc:
|
| 109 |
raise gr.Error(f"Could not read the audio file: {exc}") from exc
|
| 110 |
+
if info.frames == 0:
|
| 111 |
+
raise gr.Error("The audio file is empty.")
|
| 112 |
|
| 113 |
return denoise(input_audio_path, noise_atten_db)
|
| 114 |
|
|
|
|
| 135 |
# Slider strength
|
| 136 |
out = apply_attenuation_db(noisy, enhanced, noise_atten_db, max_db=30.0)
|
| 137 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
if original_sr != sr:
|
| 139 |
out = librosa.resample(out, orig_sr=sr, target_sr=original_sr)
|
| 140 |
|
|
|
|
| 143 |
if original_channels > 1:
|
| 144 |
out = np.tile(out[:, None], (1, original_channels))
|
| 145 |
|
| 146 |
+
input_ext = Path(input_audio_path).suffix.lower()
|
| 147 |
+
out_path = Path(tempfile.mkdtemp()) / f"denoised{input_ext if input_ext in LOSSLESS else '.wav'}"
|
| 148 |
sf.write(str(out_path), out, original_sr)
|
| 149 |
return str(out_path)
|
| 150 |
|
|
|
|
| 181 |
process_fn=process_fn,
|
| 182 |
)
|
| 183 |
|
| 184 |
+
demo.queue().launch(show_error=True, pwa=True)
|
requirements.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
deepfilternet==0.5.6
|
| 2 |
-
torch==2.
|
| 3 |
-
torchaudio==2.
|
| 4 |
numpy==1.26.4
|
| 5 |
soundfile==0.13.1
|
| 6 |
librosa==0.11.0
|
|
|
|
| 1 |
deepfilternet==0.5.6
|
| 2 |
+
torch==2.8.0
|
| 3 |
+
torchaudio==2.8.0
|
| 4 |
numpy==1.26.4
|
| 5 |
soundfile==0.13.1
|
| 6 |
librosa==0.11.0
|