Spaces:
Running on Zero
Running on Zero
Download app.py from teamup-tech/Demucs: direct link, hf CLI and curl.
- Browser
- Download file 5.56 kB
-
https://huggingface.co/spaces/teamup-tech/Demucs/resolve/main/app.py
- Command line
-
hf download hf://spaces/teamup-tech/Demucs/app.py
-
curl -L -o app.py https://huggingface.co/spaces/teamup-tech/Demucs/resolve/main/app.py
5.56 kB
| import spaces | |
| from pyharp.core import ModelCard, build_endpoint | |
| from pyharp.media.audio import save_audio | |
| from pyharp.labels import LabelList | |
| from pyharp.tags import Subcategory | |
| from audiotools import AudioSignal | |
| from demucs import pretrained | |
| from demucs.apply import apply_model | |
| import gradio as gr | |
| import torch | |
| from pathlib import Path | |
| import numpy as np | |
| # ModelCard | |
| model_card = ModelCard( | |
| name="Demucs", | |
| description=( | |
| "Separates a full music track into four individual audio stems: Drums, " | |
| "Bass, Vocals, Other-instrumental using the Demucs model. Available " | |
| "models are - htdemucs: Best overall fidelity and natural sound, " | |
| "mdx_extra: Balanced trade-off, faster processing with a small drop in " | |
| "quality, mdx_extra_q: Fastest & lightest, noticeable quality loss, " | |
| "ideal for quick previews. Repository: " | |
| "https://github.com/facebookresearch/demucs Paper: " | |
| "https://arxiv.org/abs/2111.03600" | |
| ), | |
| author="Simon Rouard, Francisco Massa, Alexandre Défossez", | |
| tags=[Subcategory.STEM_SEPARATION, 'stems'] | |
| ) | |
| DEMUX_MODELS = ["htdemucs", "mdx_extra", "mdx_extra_q"] | |
| STEM_NAMES = ["Drums", "Bass", "Vocals", "Other"] | |
| # Global model cache | |
| LOADED_MODELS = {} | |
| def get_cached_model(model_name: str): | |
| if model_name not in LOADED_MODELS: | |
| model = pretrained.get_model(model_name) | |
| model.eval() | |
| device = "cuda" if torch.cuda.is_available() else "cpu" | |
| model.to(device) | |
| LOADED_MODELS[model_name] = model | |
| return LOADED_MODELS[model_name] | |
| # Separation Logic | |
| def separate_all_stems(audio_file_path: str, model_name: str): | |
| model = get_cached_model(model_name) | |
| signal = AudioSignal(audio_file_path) | |
| signal = signal.resample(44100) # expects 44.1kHz | |
| is_mono = signal.num_channels == 1 | |
| if is_mono: | |
| signal.audio_data = signal.audio_data.repeat(1, 2, 1) # [batch, channels, samples] | |
| signal._num_channels = 2 | |
| sr = signal.sample_rate | |
| # Ensure audio_data is a torch.Tensor | |
| audio = signal.audio_data | |
| if isinstance(audio, np.ndarray): | |
| audio = torch.from_numpy(audio) | |
| audio = audio.float() # [channels, samples] or [channels, samples, ?] | |
| # Remove extra trailing dimensions | |
| if audio.ndim > 2: | |
| audio = audio.squeeze() | |
| # Final shape: [1, channels, samples] | |
| waveform = audio.unsqueeze(0) | |
| with torch.no_grad(): | |
| stems_batch = apply_model( | |
| model, | |
| waveform.to(next(model.parameters()).device), | |
| overlap=0.2, | |
| shifts=1, | |
| split=True, | |
| ) | |
| stems = stems_batch[0] | |
| output_signals = [] | |
| for stem in stems: | |
| if is_mono: | |
| stem = stem.mean(dim=0, keepdim=True) | |
| signal = AudioSignal(stem.cpu().numpy().astype("float32"), sample_rate=sr) | |
| output_signals.append(signal) | |
| # Combine drums + bass + other = instrumental | |
| drums, bass, other, vocals = output_signals | |
| instrumental = drums + bass + other | |
| return [drums, bass, vocals, other] | |
| # Process Function | |
| def process_fn(audio_file_path, model_name): | |
| try: | |
| if model_name not in DEMUX_MODELS: | |
| raise ValueError(f"Unsupported model selected: {model_name}") | |
| output_signals = separate_all_stems(audio_file_path, model_name) | |
| is_mp3 = Path(audio_file_path).suffix.lower() == ".mp3" | |
| extension = "mp3" if is_mp3 else "wav" | |
| outputs = [] | |
| for stem_name, signal in zip(STEM_NAMES, output_signals): | |
| filename = f"demucs_{model_name}_{stem_name.lower().replace(' ', '_')}.{extension}" | |
| output_path = Path(filename) | |
| save_audio(signal, output_path) | |
| outputs.append(str(output_path)) | |
| return tuple(outputs) | |
| except gr.Error: | |
| raise | |
| except Exception as e: | |
| raise gr.Error(str(e)) | |
| # Gradio App | |
| if __name__ == "__main__": | |
| with gr.Blocks() as demo: | |
| input_audio = gr.Audio(type="filepath", label="Input Audio").harp_required(True).set_info(( | |
| "The mixture to separate. Mono input is duplicated to stereo first." | |
| )) | |
| model_dropdown = gr.Dropdown( | |
| choices=DEMUX_MODELS, | |
| label="Demucs Model", | |
| value="htdemucs", | |
| info=( | |
| "Choose which Demucs variant to use for separation:\n" | |
| "1. htdemucs: High-quality Hybrid Transformer Demucs trained on MusDB + 800 songs.\n" | |
| "2. mdx_extra: Trained with extended data. Balanced model offering a clear trade-off between quality and speed.\n" | |
| "3. mdx_extra_q: Quantized lightweight version of mdx_extra.\n" | |
| ) | |
| ) | |
| output_drums = gr.Audio(type="filepath", label="Drums").set_info("The isolated drums.") | |
| output_bass = gr.Audio(type="filepath", label="Bass").set_info("The isolated bass.") | |
| output_vocals = gr.Audio(type="filepath", label="Vocals").set_info("The isolated vocal.") | |
| output_instrumental = gr.Audio(type="filepath", label="Other").set_info(( | |
| "Everything the other three stems did not take." | |
| )) | |
| app = build_endpoint( | |
| model_card=model_card, | |
| input_components=[input_audio, model_dropdown], | |
| output_components=[ | |
| output_drums, | |
| output_bass, | |
| output_vocals, | |
| output_instrumental | |
| ], | |
| process_fn=process_fn | |
| ) | |
| demo.queue().launch(pwa=True, show_error=True) | |