Nekochu commited on
Commit
ddabbfb
·
verified ·
1 Parent(s): 9eec651
Files changed (5) hide show
  1. .gitattributes +1 -0
  2. README.md +69 -5
  3. app.py +111 -0
  4. example.wav +3 -0
  5. requirements.txt +7 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ 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
+ example.wav filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,12 +1,76 @@
1
  ---
2
- title: Demucs
3
- emoji: 📚
4
- colorFrom: green
5
- colorTo: yellow
6
  sdk: gradio
7
  sdk_version: 6.3.0
 
8
  app_file: app.py
9
  pinned: false
 
 
 
 
 
 
 
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Demucs Stem Separator
3
+ emoji: 🎵
4
+ colorFrom: purple
5
+ colorTo: pink
6
  sdk: gradio
7
  sdk_version: 6.3.0
8
+ python_version: "3.10"
9
  app_file: app.py
10
  pinned: false
11
+ license: mit
12
+ tags:
13
+ - audio-separation
14
+ - stem-separation
15
+ - demucs
16
+ - mcp-server
17
+ short_description: Separate audio into vocals, drums, bass, other stems
18
  ---
19
 
20
+ # 🎵 Demucs Stem Separator
21
+
22
+ Separate audio → **Vocals** | **Drums** | **Bass** | **Other**
23
+
24
+ | Stem | Output |
25
+ |------|--------|
26
+ | 🎤 Vocals | Singing, voice |
27
+ | 🥁 Drums | Percussion |
28
+ | 🎸 Bass | Low frequencies |
29
+ | 🎹 Other | Guitars, synths |
30
+
31
+ **Formats:** MP3, WAV, FLAC, OGG, M4A
32
+
33
+ ---
34
+
35
+ ## API
36
+
37
+ ### Python Client
38
+
39
+ ```python
40
+ from gradio_client import Client, handle_file
41
+
42
+ client = Client("Luminia/demucs")
43
+ vocals, drums, bass, other = client.predict(
44
+ audio_file=handle_file("song.wav"),
45
+ model_name="htdemucs", # or "htdemucs_ft"
46
+ api_name="/separate"
47
+ )
48
+ ```
49
+
50
+ ### REST API (curl)
51
+
52
+ ```bash
53
+ # Submit
54
+ curl -X POST "https://luminia-demucs.hf.space/gradio_api/call/separate" \
55
+ -H "Content-Type: application/json" \
56
+ -d '{"data": ["https://example.com/song.mp3", "htdemucs"]}'
57
+
58
+ # Get result (use event_id from response)
59
+ curl "https://luminia-demucs.hf.space/gradio_api/call/separate/{event_id}"
60
+ ```
61
+
62
+ ### MCP
63
+
64
+ ```json
65
+ {
66
+ "mcpServers": {
67
+ "demucs": {
68
+ "url": "https://luminia-demucs.hf.space/gradio_api/mcp/"
69
+ }
70
+ }
71
+ }
72
+ ```
73
+
74
+ ---
75
+
76
+ [Demucs](https://github.com/facebookresearch/demucs) by Facebook Research
app.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Demucs Audio Stem Separator - Separate audio into vocals, drums, bass, other."""
2
+ import os
3
+ import tempfile
4
+ import gradio as gr
5
+ import torch
6
+ import soundfile as sf
7
+ import numpy as np
8
+ import librosa
9
+ from demucs.pretrained import get_model
10
+ from demucs.apply import apply_model
11
+
12
+ device = torch.device("cpu")
13
+ _model_cache = {}
14
+
15
+
16
+ def load_model(model_name: str):
17
+ """Load and cache Demucs model."""
18
+ if model_name not in _model_cache:
19
+ model = get_model(model_name)
20
+ model.to(device).eval()
21
+ _model_cache[model_name] = model
22
+ return _model_cache[model_name]
23
+
24
+
25
+ def separate_stems(audio_file: str, model_name: str = "htdemucs", progress=gr.Progress()) -> tuple[str, str, str, str]:
26
+ """
27
+ Separate audio into stems using Demucs.
28
+
29
+ Args:
30
+ audio_file: Path to the input audio file (MP3, WAV, FLAC, OGG, M4A)
31
+ model_name: Demucs model - htdemucs (fast) or htdemucs_ft (better quality)
32
+
33
+ Returns:
34
+ Tuple of paths to separated stems: (vocals, drums, bass, other)
35
+ """
36
+ if audio_file is None:
37
+ raise gr.Error("Please upload an audio file")
38
+
39
+ progress(0.1, desc="Loading model...")
40
+ model = load_model(model_name)
41
+
42
+ progress(0.2, desc="Loading audio...")
43
+ audio_data, sr = sf.read(audio_file)
44
+ if audio_data.ndim == 1:
45
+ waveform = torch.from_numpy(audio_data.astype(np.float32)).unsqueeze(0)
46
+ else:
47
+ waveform = torch.from_numpy(audio_data.T.astype(np.float32))
48
+
49
+ target_sr = model.samplerate
50
+ if sr != target_sr:
51
+ progress(0.25, desc="Resampling...")
52
+ waveform_np = waveform.numpy()
53
+ resampled = [librosa.resample(waveform_np[ch], orig_sr=sr, target_sr=target_sr)
54
+ for ch in range(waveform_np.shape[0])]
55
+ waveform = torch.from_numpy(np.stack(resampled))
56
+
57
+ if waveform.shape[0] == 1:
58
+ waveform = waveform.repeat(2, 1)
59
+ elif waveform.shape[0] > 2:
60
+ waveform = waveform[:2, :]
61
+
62
+ waveform = waveform.unsqueeze(0).to(device)
63
+
64
+ progress(0.3, desc="Separating stems...")
65
+ with torch.no_grad():
66
+ sources = apply_model(model, waveform, device=device, progress=True)
67
+
68
+ progress(0.9, desc="Saving...")
69
+ output_dir = tempfile.mkdtemp()
70
+ output_files = {}
71
+ for i, name in enumerate(model.sources):
72
+ stem = sources[0, i].cpu().numpy()
73
+ path = os.path.join(output_dir, f"{name}.wav")
74
+ sf.write(path, stem.T, target_sr)
75
+ output_files[name] = path
76
+
77
+ return output_files.get("vocals"), output_files.get("drums"), output_files.get("bass"), output_files.get("other")
78
+
79
+
80
+ with gr.Blocks(title="Demucs Stem Separator") as demo:
81
+ gr.Markdown("# 🎵 Demucs Stem Separator\nSeparate audio → **Vocals** | **Drums** | **Bass** | **Other**")
82
+
83
+ with gr.Row():
84
+ with gr.Column(scale=1):
85
+ audio_input = gr.Audio(label="Audio File", type="filepath", sources=["upload"])
86
+ model_choice = gr.Dropdown(["htdemucs", "htdemucs_ft"], value="htdemucs", label="Model",
87
+ info="htdemucs: Fast | htdemucs_ft: Better quality")
88
+ separate_btn = gr.Button("Separate Stems", variant="primary", size="lg")
89
+
90
+ with gr.Column(scale=2):
91
+ with gr.Row():
92
+ vocals = gr.Audio(label="🎤 Vocals", type="filepath")
93
+ drums = gr.Audio(label="🥁 Drums", type="filepath")
94
+ with gr.Row():
95
+ bass = gr.Audio(label="🎸 Bass", type="filepath")
96
+ other = gr.Audio(label="🎹 Other", type="filepath")
97
+
98
+ separate_btn.click(separate_stems, [audio_input, model_choice], [vocals, drums, bass, other], api_name="separate")
99
+
100
+ gr.Examples(
101
+ examples=[["example.wav", "htdemucs"]],
102
+ inputs=[audio_input, model_choice],
103
+ outputs=[vocals, drums, bass, other],
104
+ fn=separate_stems,
105
+ cache_examples=True,
106
+ cache_mode="lazy",
107
+ label="Example"
108
+ )
109
+
110
+ if __name__ == "__main__":
111
+ demo.launch(mcp_server=True, show_error=True)
example.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:837226b7f3ec5a51a5bb735c0aca0f354864001124aa535dc74f20af6ac14454
3
+ size 2872398
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ --extra-index-url https://download.pytorch.org/whl/cpu
2
+ torch
3
+ demucs
4
+ gradio>=6.3.0
5
+ numpy
6
+ soundfile
7
+ librosa