Spaces:
Running on Zero
Running on Zero
feat(deploy): m7 — hf spaces readme + cache bootstrap + zerogpu decorator
Browse files- README.md +28 -28
- app.py +81 -12
- pyproject.toml +4 -0
- requirements.txt +4 -0
README.md
CHANGED
|
@@ -1,29 +1,22 @@
|
|
| 1 |
---
|
| 2 |
title: ACE Music Studio
|
| 3 |
-
emoji:
|
| 4 |
colorFrom: gray
|
| 5 |
colorTo: gray
|
| 6 |
sdk: gradio
|
| 7 |
-
sdk_version:
|
| 8 |
app_file: app.py
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
preload_from_hub:
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
- facebook/htdemucs_ft *.th
|
| 16 |
---
|
| 17 |
|
| 18 |
# ACE Music Studio
|
| 19 |
|
| 20 |
-
A single-process Gradio app that wraps [ACE-Step 1.5 XL SFT](https://github.com/ace-step/ACE-Step-1.5) for full-song generation with vocals, with
|
| 21 |
-
|
| 22 |
-
[](https://huggingface.co/spaces/techfreakworm/ace-music-studio)
|
| 23 |
-
[](https://github.com/techfreakworm/ace-music-studio/stargazers)
|
| 24 |
-
[](LICENSE)
|
| 25 |
-
[](pyproject.toml)
|
| 26 |
-
[](https://github.com/ace-step/ACE-Step-1.5)
|
| 27 |
|
| 28 |
→ **Live demo:** https://huggingface.co/spaces/techfreakworm/ace-music-studio
|
| 29 |
|
|
@@ -31,7 +24,7 @@ A single-process Gradio app that wraps [ACE-Step 1.5 XL SFT](https://github.com/
|
|
| 31 |
|
| 32 |
## What's inside
|
| 33 |
|
| 34 |
-
Five
|
| 35 |
|
| 36 |
| Mode | Inputs | What it does |
|
| 37 |
|---|---|---|
|
|
@@ -41,38 +34,43 @@ Five tabs. One ACE-Step pipeline underneath. Progressive disclosure — defaults
|
|
| 41 |
| **Edit** | source audio + segment + target lyrics | Repaint a segment OR flow-morph caption-to-caption |
|
| 42 |
| **Lyrics** | brief + structure | Qwen 2.5 7B drafts structurally-tagged lyrics |
|
| 43 |
|
| 44 |
-
Every song
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
---
|
| 47 |
|
| 48 |
-
##
|
| 49 |
|
| 50 |
-
Requires **Python 3.11**, ~32 GB free disk for weights, and **128 GB unified memory recommended on Apple Silicon** (M5 Max ideal; M3 Max+ workable).
|
| 51 |
|
| 52 |
```bash
|
| 53 |
git clone https://github.com/techfreakworm/ace-music-studio
|
| 54 |
cd ace-music-studio
|
| 55 |
-
bash setup.sh
|
| 56 |
source .venv/bin/activate
|
| 57 |
-
python app.py
|
| 58 |
```
|
| 59 |
|
| 60 |
-
|
| 61 |
|
| 62 |
-
|
| 63 |
|
| 64 |
-
##
|
| 65 |
|
| 66 |
```bash
|
| 67 |
-
git remote add space https://huggingface.co/spaces/
|
| 68 |
git push space main
|
| 69 |
```
|
| 70 |
|
| 71 |
-
`preload_from_hub`
|
| 72 |
|
| 73 |
## Architecture
|
| 74 |
|
| 75 |
-
See [`docs/superpowers/specs/2026-05-18-ace-music-studio-design.md`](docs/superpowers/specs/2026-05-18-ace-music-studio-design.md) for the full design
|
| 76 |
|
| 77 |
## License
|
| 78 |
|
|
@@ -80,4 +78,6 @@ MIT for the app code (see `LICENSE`). ACE-Step 1.5 XL SFT, Qwen 2.5 7B Instruct,
|
|
| 80 |
|
| 81 |
## Credits
|
| 82 |
|
| 83 |
-
ACE-Step by [ACE Studio × StepFun](https://ace-step.github.io/). Apple Silicon port by [clockworksquirrel](https://github.com/clockworksquirrel/ace-step-apple-silicon). Qwen 2.5 by [Alibaba](https://huggingface.co/Qwen). Demucs by [Meta AI](https://github.com/facebookresearch/demucs).
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
title: ACE Music Studio
|
| 3 |
+
emoji: 🎵
|
| 4 |
colorFrom: gray
|
| 5 |
colorTo: gray
|
| 6 |
sdk: gradio
|
| 7 |
+
sdk_version: 6.14.0
|
| 8 |
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
license: mit
|
| 11 |
+
short_description: Open-source song generation studio on ACE-Step 1.5 XL SFT — Generate, Cover, Extend, Edit, draft Lyrics.
|
| 12 |
preload_from_hub:
|
| 13 |
+
- ACE-Step/Ace-Step1.5 vae/diffusion_pytorch_model.safetensors,vae/config.json,encoder/pytorch_model.bin,encoder/config.json,encoder/tokenizer.json
|
| 14 |
+
- ACE-Step/acestep-v15-xl-sft model.safetensors
|
|
|
|
| 15 |
---
|
| 16 |
|
| 17 |
# ACE Music Studio
|
| 18 |
|
| 19 |
+
A single-process Gradio app that wraps [ACE-Step 1.5 XL SFT](https://github.com/ace-step/ACE-Step-1.5) for full-song generation with vocals, with Qwen 2.5 for lyrics drafting and Demucs for stem separation. Runs locally on **Apple Silicon (MPS+MLX)** or **NVIDIA (CUDA)**, deploys to **Hugging Face Spaces (ZeroGPU)**.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
→ **Live demo:** https://huggingface.co/spaces/techfreakworm/ace-music-studio
|
| 22 |
|
|
|
|
| 24 |
|
| 25 |
## What's inside
|
| 26 |
|
| 27 |
+
Five modes. One ACE-Step pipeline underneath. Progressive disclosure — defaults stay short and reveal advanced controls only when asked.
|
| 28 |
|
| 29 |
| Mode | Inputs | What it does |
|
| 30 |
|---|---|---|
|
|
|
|
| 34 |
| **Edit** | source audio + segment + target lyrics | Repaint a segment OR flow-morph caption-to-caption |
|
| 35 |
| **Lyrics** | brief + structure | Qwen 2.5 7B drafts structurally-tagged lyrics |
|
| 36 |
|
| 37 |
+
Every song mode supports a single stacked LoRA — bundled presets plus arbitrary `.safetensors` uploads. The preset registry ships with the official ACE-Step LoRAs published on HF:
|
| 38 |
+
|
| 39 |
+
- [ACE-Step/ACE-Step-v1-chinese-rap-LoRA](https://huggingface.co/ACE-Step/ACE-Step-v1-chinese-rap-LoRA)
|
| 40 |
+
- [ACE-Step/ACE-Step-v1.5-chinese-new-year-LoRA](https://huggingface.co/ACE-Step/ACE-Step-v1.5-chinese-new-year-LoRA)
|
| 41 |
+
|
| 42 |
+
After every song generation, three post-process actions sit beneath the player: **Demucs stem separation**, **pyloudnorm normalisation to -14 LUFS**, and **MP3 320k export via ffmpeg**.
|
| 43 |
|
| 44 |
---
|
| 45 |
|
| 46 |
+
## Local setup
|
| 47 |
|
| 48 |
+
Requires **Python 3.11**, ~32 GB free disk for weights, and **128 GB unified memory recommended on Apple Silicon** (M5 Max ideal; M3 Max+ workable). On NVIDIA, ~24 GB VRAM.
|
| 49 |
|
| 50 |
```bash
|
| 51 |
git clone https://github.com/techfreakworm/ace-music-studio
|
| 52 |
cd ace-music-studio
|
| 53 |
+
bash setup.sh # creates .venv, installs requirements
|
| 54 |
source .venv/bin/activate
|
| 55 |
+
python app.py # http://127.0.0.1:7860
|
| 56 |
```
|
| 57 |
|
| 58 |
+
`setup.sh` detects Apple Silicon and adds `requirements-mac.txt` (MLX-LM + the [`clockworksquirrel/ace-step-apple-silicon`](https://github.com/clockworksquirrel/ace-step-apple-silicon) fork). First launch downloads weights into your HF cache (`~/.cache/huggingface/hub/`).
|
| 59 |
|
| 60 |
+
`PYTORCH_ENABLE_MPS_FALLBACK=1` is set automatically by `app.py` so the few MPS-unsupported ops degrade to CPU.
|
| 61 |
|
| 62 |
+
## HF Spaces deploy
|
| 63 |
|
| 64 |
```bash
|
| 65 |
+
git remote add space https://huggingface.co/spaces/<your-handle>/ace-music-studio
|
| 66 |
git push space main
|
| 67 |
```
|
| 68 |
|
| 69 |
+
`preload_from_hub` (this README's frontmatter) pre-downloads the ACE-Step 1.5 XL SFT umbrella weights at build time. `app._bootstrap_spaces_cache()` runs once at module init when `SPACE_ID` is set, symlinking the HF cache into the fork's expected `<site-packages>/checkpoints/` layout so the pipeline finds them on first request. `@spaces.GPU(duration=180)` decorates the click handlers — on Spaces it gates them to a ZeroGPU worker, locally it's a no-op.
|
| 70 |
|
| 71 |
## Architecture
|
| 72 |
|
| 73 |
+
See [`docs/superpowers/specs/2026-05-18-ace-music-studio-design.md`](docs/superpowers/specs/2026-05-18-ace-music-studio-design.md) for the full design and [`docs/superpowers/plans/2026-05-18-ace-music-studio.md`](docs/superpowers/plans/2026-05-18-ace-music-studio.md) for the implementation plan.
|
| 74 |
|
| 75 |
## License
|
| 76 |
|
|
|
|
| 78 |
|
| 79 |
## Credits
|
| 80 |
|
| 81 |
+
ACE-Step by [ACE Studio × StepFun](https://ace-step.github.io/). Apple Silicon port by [clockworksquirrel](https://github.com/clockworksquirrel/ace-step-apple-silicon). Qwen 2.5 by [Alibaba](https://huggingface.co/Qwen). Demucs by [Meta AI](https://github.com/facebookresearch/demucs).
|
| 82 |
+
|
| 83 |
+
Made with ❤️ by [Mayank Gupta](https://huggingface.co/techfreakworm).
|
app.py
CHANGED
|
@@ -28,9 +28,10 @@ state gives us the sidebar "active item" highlight for free via CSS
|
|
| 28 |
DO NOT switch this back to ``gr.Tabs`` — that produces top-positioned
|
| 29 |
horizontal tabs which contradicts the wireframes.
|
| 30 |
|
| 31 |
-
On HF Spaces
|
| 32 |
-
|
| 33 |
-
|
|
|
|
| 34 |
"""
|
| 35 |
|
| 36 |
from __future__ import annotations
|
|
@@ -67,6 +68,78 @@ def get_backend() -> be.ACEStepStudioBackend:
|
|
| 67 |
return _BACKEND
|
| 68 |
|
| 69 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
def _safe_call(fn, *args, **kwargs):
|
| 71 |
"""Wrap a mode handler so all known exceptions become friendly gr.Error toasts.
|
| 72 |
|
|
@@ -190,6 +263,7 @@ def on_lora_strength_change(state, strength: float):
|
|
| 190 |
return new_state, _active_md(new_state["name"], float(strength), kind)
|
| 191 |
|
| 192 |
|
|
|
|
| 193 |
def on_generate_click(
|
| 194 |
prompt: str,
|
| 195 |
lyrics: str,
|
|
@@ -218,6 +292,7 @@ def on_generate_click(
|
|
| 218 |
return out_path, meta, new_history
|
| 219 |
|
| 220 |
|
|
|
|
| 221 |
def on_cover_click(
|
| 222 |
ref_audio,
|
| 223 |
prompt: str,
|
|
@@ -249,6 +324,7 @@ def on_cover_click(
|
|
| 249 |
return out_path, meta, new_history
|
| 250 |
|
| 251 |
|
|
|
|
| 252 |
def on_extend_click(
|
| 253 |
seed_audio,
|
| 254 |
extra_prompt: str,
|
|
@@ -288,6 +364,7 @@ def on_extend_click(
|
|
| 288 |
return out_path, meta, new_history
|
| 289 |
|
| 290 |
|
|
|
|
| 291 |
def on_draft_lyrics(
|
| 292 |
brief: str,
|
| 293 |
structure: str,
|
|
@@ -365,6 +442,7 @@ def on_export_mp3(audio_path):
|
|
| 365 |
return gr.File(value=str(out), visible=True)
|
| 366 |
|
| 367 |
|
|
|
|
| 368 |
def on_edit_click(
|
| 369 |
source_audio,
|
| 370 |
sub_mode: str,
|
|
@@ -495,14 +573,6 @@ MODE_CHOICES = [
|
|
| 495 |
]
|
| 496 |
|
| 497 |
|
| 498 |
-
def _bootstrap() -> None:
|
| 499 |
-
"""HF Spaces: mirror read-only preload cache into a writable tree.
|
| 500 |
-
|
| 501 |
-
Local Mac/CUDA: no-op. Implemented at M7 when we wire deployment.
|
| 502 |
-
"""
|
| 503 |
-
pass
|
| 504 |
-
|
| 505 |
-
|
| 506 |
def build_app() -> gr.Blocks:
|
| 507 |
device = ace_pipeline.detect_device()
|
| 508 |
|
|
@@ -762,7 +832,6 @@ def build_app() -> gr.Blocks:
|
|
| 762 |
|
| 763 |
|
| 764 |
if __name__ == "__main__":
|
| 765 |
-
_bootstrap()
|
| 766 |
demo = build_app()
|
| 767 |
demo.queue(default_concurrency_limit=1)
|
| 768 |
demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))
|
|
|
|
| 28 |
DO NOT switch this back to ``gr.Tabs`` — that produces top-positioned
|
| 29 |
horizontal tabs which contradicts the wireframes.
|
| 30 |
|
| 31 |
+
On HF Spaces (``SPACE_ID`` env present), ``_bootstrap_spaces_cache()``
|
| 32 |
+
runs once on import to symlink HF hub cache snapshots into the
|
| 33 |
+
acestep-apple-silicon fork's ``<site-packages>/checkpoints/`` layout.
|
| 34 |
+
On Mac/Linux locally, it's a no-op.
|
| 35 |
"""
|
| 36 |
|
| 37 |
from __future__ import annotations
|
|
|
|
| 68 |
return _BACKEND
|
| 69 |
|
| 70 |
|
| 71 |
+
def _bootstrap_spaces_cache() -> None:
|
| 72 |
+
"""On HF Spaces, mirror the HF hub cache into the site-packages checkpoints/ dir.
|
| 73 |
+
|
| 74 |
+
The acestep-apple-silicon fork resolves checkpoints relative to its own install
|
| 75 |
+
location (``.venv/.../site-packages/checkpoints/``). HF Spaces puts model weights
|
| 76 |
+
in the HF hub cache. This bootstrap snapshot-downloads the two required repos
|
| 77 |
+
into the cache (using preload mirror if available) and then symlinks each
|
| 78 |
+
snapshot child into ``checkpoints/`` so the fork's resolver finds them.
|
| 79 |
+
|
| 80 |
+
Local Mac/CUDA: no-op (guarded by ``SPACE_ID`` env var).
|
| 81 |
+
"""
|
| 82 |
+
if not os.getenv("SPACE_ID"):
|
| 83 |
+
return
|
| 84 |
+
|
| 85 |
+
import site
|
| 86 |
+
|
| 87 |
+
from huggingface_hub import snapshot_download
|
| 88 |
+
|
| 89 |
+
site_pkgs = site.getsitepackages()[0]
|
| 90 |
+
target_dir = Path(site_pkgs) / "checkpoints"
|
| 91 |
+
|
| 92 |
+
if target_dir.exists():
|
| 93 |
+
return # already bootstrapped
|
| 94 |
+
|
| 95 |
+
hf_home = os.getenv("HF_HOME", "/home/user/.cache/huggingface")
|
| 96 |
+
|
| 97 |
+
# Download Ace-Step1.5 umbrella (vae + encoder).
|
| 98 |
+
umbrella_path = snapshot_download(
|
| 99 |
+
repo_id="ACE-Step/Ace-Step1.5",
|
| 100 |
+
cache_dir=hf_home,
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
# Download the XL SFT diffusion variant.
|
| 104 |
+
xl_sft_path = snapshot_download(
|
| 105 |
+
repo_id="ACE-Step/acestep-v15-xl-sft",
|
| 106 |
+
cache_dir=hf_home,
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
# Merge both snapshots into ``checkpoints/`` via symlinks.
|
| 110 |
+
target_dir.mkdir(parents=True, exist_ok=True)
|
| 111 |
+
for src_path in [umbrella_path, xl_sft_path]:
|
| 112 |
+
for child in Path(src_path).iterdir():
|
| 113 |
+
link = target_dir / child.name
|
| 114 |
+
if not link.exists():
|
| 115 |
+
link.symlink_to(child)
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def _maybe_spaces_gpu():
|
| 119 |
+
"""Return ``@spaces.GPU(duration=180)`` on HF Spaces, otherwise a no-op decorator.
|
| 120 |
+
|
| 121 |
+
The decorator MUST be applied at module load time — ZeroGPU's startup
|
| 122 |
+
analyzer doesn't see runtime decoration. Local dev is a transparent pass-through.
|
| 123 |
+
"""
|
| 124 |
+
if os.getenv("SPACE_ID"):
|
| 125 |
+
try:
|
| 126 |
+
import spaces
|
| 127 |
+
|
| 128 |
+
return spaces.GPU(duration=180)
|
| 129 |
+
except ImportError:
|
| 130 |
+
pass
|
| 131 |
+
|
| 132 |
+
def _noop(fn):
|
| 133 |
+
return fn
|
| 134 |
+
|
| 135 |
+
return _noop
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
# Run cache bootstrap at module import so HF Spaces' startup analyzer sees
|
| 139 |
+
# the symlinks before the lazy backend singleton is constructed on first click.
|
| 140 |
+
_bootstrap_spaces_cache()
|
| 141 |
+
|
| 142 |
+
|
| 143 |
def _safe_call(fn, *args, **kwargs):
|
| 144 |
"""Wrap a mode handler so all known exceptions become friendly gr.Error toasts.
|
| 145 |
|
|
|
|
| 263 |
return new_state, _active_md(new_state["name"], float(strength), kind)
|
| 264 |
|
| 265 |
|
| 266 |
+
@_maybe_spaces_gpu()
|
| 267 |
def on_generate_click(
|
| 268 |
prompt: str,
|
| 269 |
lyrics: str,
|
|
|
|
| 292 |
return out_path, meta, new_history
|
| 293 |
|
| 294 |
|
| 295 |
+
@_maybe_spaces_gpu()
|
| 296 |
def on_cover_click(
|
| 297 |
ref_audio,
|
| 298 |
prompt: str,
|
|
|
|
| 324 |
return out_path, meta, new_history
|
| 325 |
|
| 326 |
|
| 327 |
+
@_maybe_spaces_gpu()
|
| 328 |
def on_extend_click(
|
| 329 |
seed_audio,
|
| 330 |
extra_prompt: str,
|
|
|
|
| 364 |
return out_path, meta, new_history
|
| 365 |
|
| 366 |
|
| 367 |
+
@_maybe_spaces_gpu()
|
| 368 |
def on_draft_lyrics(
|
| 369 |
brief: str,
|
| 370 |
structure: str,
|
|
|
|
| 442 |
return gr.File(value=str(out), visible=True)
|
| 443 |
|
| 444 |
|
| 445 |
+
@_maybe_spaces_gpu()
|
| 446 |
def on_edit_click(
|
| 447 |
source_audio,
|
| 448 |
sub_mode: str,
|
|
|
|
| 573 |
]
|
| 574 |
|
| 575 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 576 |
def build_app() -> gr.Blocks:
|
| 577 |
device = ace_pipeline.detect_device()
|
| 578 |
|
|
|
|
| 832 |
|
| 833 |
|
| 834 |
if __name__ == "__main__":
|
|
|
|
| 835 |
demo = build_app()
|
| 836 |
demo.queue(default_concurrency_limit=1)
|
| 837 |
demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))
|
pyproject.toml
CHANGED
|
@@ -10,6 +10,10 @@ requires-python = ">=3.11,<3.12"
|
|
| 10 |
[tool.ruff]
|
| 11 |
line-length = 110
|
| 12 |
target-version = "py311"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
[tool.ruff.lint]
|
| 15 |
select = ["E", "F", "I", "B", "UP", "RUF"]
|
|
|
|
| 10 |
[tool.ruff]
|
| 11 |
line-length = 110
|
| 12 |
target-version = "py311"
|
| 13 |
+
# checkpoints/ holds downloaded model code shipped by upstream HF repos —
|
| 14 |
+
# linting third-party drop-ins isn't our concern. output/ and research/
|
| 15 |
+
# are runtime artefacts / scratch.
|
| 16 |
+
extend-exclude = ["checkpoints", "output", "research", ".venv"]
|
| 17 |
|
| 18 |
[tool.ruff.lint]
|
| 19 |
select = ["E", "F", "I", "B", "UP", "RUF"]
|
requirements.txt
CHANGED
|
@@ -11,3 +11,7 @@ librosa>=0.10
|
|
| 11 |
huggingface_hub>=0.25
|
| 12 |
numpy>=1.26,<2
|
| 13 |
ace-step @ git+https://github.com/ace-step/ACE-Step-1.5.git
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
huggingface_hub>=0.25
|
| 12 |
numpy>=1.26,<2
|
| 13 |
ace-step @ git+https://github.com/ace-step/ACE-Step-1.5.git
|
| 14 |
+
# HF Spaces ZeroGPU. Do NOT pin — HF's ZeroGPU build injects its own version
|
| 15 |
+
# and a pin causes pip-resolve failure. Locally the `import spaces` in
|
| 16 |
+
# app._maybe_spaces_gpu() is wrapped in try/except so absence is fine.
|
| 17 |
+
spaces; sys_platform == "linux"
|