Download a YouTube video, extract its audio, and split it into stems (vocals, drums, bass, other, plus a vocals/instrumental 2-stem mix) using Demucs. Built for Windows with an AMD GPU (ROCm).
Given a YouTube URL, run.ps1 (or run.bat) will:
- Download the best-quality video+audio with
yt-dlpintovideo\<title>\. - Extract the audio track to a 24-bit WAV with
ffmpegintoaudio\<title>\. - Run Demucs (
htdemucs_ftmodel) on the WAV to separate it into 4 stems (vocals, drums, bass, other), saved toaudio\<title>\stems\. - Derive a vocals/instrumental 2-stem mix from those 4 stems (instrumental
is drums+bass+other summed) - no second Demucs pass needed - saved to
audio\<title>\stems\2stem\.
- Windows with an AMD GPU (uses ROCm nightly builds of PyTorch).
- Python 3.12.
.\run.ps1 --setup
This creates a virtual environment in venv\, installs PyTorch/torchaudio/torchvision
with ROCm support, installs the packages in requirements.txt (yt-dlp, demucs,
soundfile), and downloads a portable ffmpeg/ffprobe into tools\.
The ROCm package index used for the PyTorch install targets gfx103X-dgpu
(RDNA2, e.g. RX 6000 series) by default - edit index_url under [rocm] in
stem-splitter.ini if you have a different AMD GPU (see the comments in
that file for other targets). Only read the first time --setup installs
torch; delete venv\ and rerun --setup to pick up a change.
.\run.ps1 "<YouTube URL>"
Run from PowerShell, use
run.ps1, notrun.bat. Callingrun.batdirectly from PowerShell can silently truncate URLs at any=,&,,, or;character (a quirk of how cmd.exe tokenizes batch-file arguments), which breaks most YouTube URLs.run.ps1avoids this by invoking the venv's Python directly.run.batstill works fine fromcmd.exe.
Output layout for a video titled My Song:
video\My Song\My Song.mkv - downloaded video
audio\My Song\My Song.wav - extracted audio
audio\My Song\stems\htdemucs_ft\My Song\vocals.wav - 4-stem split
audio\My Song\stems\htdemucs_ft\My Song\drums.wav
audio\My Song\stems\htdemucs_ft\My Song\bass.wav
audio\My Song\stems\htdemucs_ft\My Song\other.wav
audio\My Song\stems\2stem\vocals.wav - 2-stem split
audio\My Song\stems\2stem\instrumental.wav
.\build.bat
Compiles build\stem-splitter.exe, a thin launcher that forwards straight to
this project's venv\Scripts\python.exe and main.py (the venv itself is
never copied - run .\run.ps1 --setup first if you haven't). Add build\ to
your PATH, then run it from anywhere:
stem-splitter "<YouTube URL>"
build\ is gitignored; rerun build.bat after moving the project folder.
Since this launcher just forwards to the project's own main.py, output still
always goes under this project's video\/audio\ folders, no matter which
directory you run stem-splitter from.
Requires .\run.ps1 --setup to have been run first (needs venv\ and
tools\ffmpeg.exe/ffprobe.exe - bundle.bat doesn't download these
itself, only packages what's already there).
.\bundle.bat
Uses PyInstaller (--onedir) to produce a fully self-contained
bundle\dist\stem-splitter\ folder - Python, torch/rocm, demucs, yt-dlp,
ffmpeg, and stem-splitter.ini all included. Unlike build.bat's launcher,
this folder doesn't need venv\ to exist afterwards and can be copied
elsewhere. Add bundle\dist\stem-splitter\ to your PATH, then run it from
anywhere:
stem-splitter "<YouTube URL>"
Since this exe is truly standalone, video\/audio\ output folders are
created under whatever directory you run it from (not the install folder) -
different from build.bat's launcher, which always writes under this
project's own folders.
bundle\ is gitignored; rerun bundle.bat after changing dependencies or
stem-splitter.ini. The bundled torch/rocm build is whichever GPU
architecture venv\ was set up with (index_url in stem-splitter.ini,
default gfx103X-dgpu), so the bundle is only portable to machines with a
matching GPU.
Requires .\run.ps1 --setup to have been run first (needs venv\ with
torch/demucs installed - demucs-bundle.bat doesn't install these itself).
.\demucs-bundle.bat
Same idea as stemrollerapp/demucs-cxfreeze:
uses cx_Freeze (not PyInstaller) to freeze just Demucs+torch - not the rest
of this app (yt-dlp, video download, 2-stem mixing) - into a single
bundle\demucs-cxfreeze\demucs-cxfreeze.exe. Entry point is
scripts\run_demucs.py, so it gets the same soundfile-based WAV/FLAC writer
main.py uses (avoids needing torchcodec/ffmpeg-codec DLLs). Behaves like
the demucs CLI:
demucs-cxfreeze -n htdemucs_ft -d cuda -o "<outdir>" "<input.wav>"
As with the upstream repo, ffmpeg/ffprobe and model files are not
bundled - put this project's tools\ folder on PATH before running it, and
either let Demucs download models on first use or pass --repo with a
folder of pre-downloaded ones. bundle\ is gitignored; rerun
demucs-bundle.bat after changing dependencies. Like bundle.bat, the
frozen torch build is whichever GPU architecture venv\ was set up with, so
it's only portable to machines with a matching GPU.
Requires Inno Setup
(winget install JRSoftware.InnoSetup) and a bundle\dist\stem-splitter\
built via .\bundle.bat first.
.\installer.bat
Produces installer\output\stem-splitter-rocm-install.exe - a
wizard installer (no admin rights needed) that lets the user pick a
destination folder, optionally adds it to their PATH, and registers a
proper uninstaller in "Apps & Features". installer\output\ is gitignored;
installer\stem-splitter.iss (the Inno Setup script) is tracked.
Three GitHub Actions workflows, all on windows-latest (this project is
Windows-only):
- .github/workflows/ci.yml - runs on every
push/PR. Installs
requirements.txt(pulling a small stock CPU torch via demucs's own dependency, not the ~3.6GB ROCm nightly build) and verifiesmain.py/scripts\*.pyimport and run cleanly,stem-splitter.iniparses, andrun.bat/build.bat/bundle.bat/installer.bat/demucs-bundle.batall parse and fail gracefully without avenv\. Fast (a couple minutes), cheap, no GPU/ROCm needed. - .github/workflows/release.yml - runs
on every published GitHub Release (or manually via "Run workflow", which
builds but skips the upload step). Does the real, expensive build:
run.bat --setup(downloads the full ROCm/torch build),bundle.bat,installer.bat, then uploadsstem-splitter-rocm-install.exeto the release withgh release upload. Takes roughly 30-45 minutes and counts 2x against your Actions minutes quota (Windows runners). Standard GitHub-hosted runners have limited free disk space, which is tight against what this build needs (venv + bundle + installer output add up to ~9-10GB) - if it starts failing on disk space, a larger or self-hosted runner may be needed. - .github/workflows/release-demucs.yml -
same trigger as
release.yml, but builds the standalonedemucs-cxfreezebundle instead:run.bat --setup,demucs-bundle.bat, zipsbundle\demucs-cxfreeze\intodemucs-cxfreeze-rocm.zip, and uploads it to the release. Independent ofrelease.yml(different job, different tool -cx_Freezeinstead of PyInstaller+Inno Setup) so a failure in one doesn't block the other. Same disk space caveat asrelease.yml.
.\run.ps1 --clean
Removes downloaded/generated files under audio\, tools\, and video\
(keeps ffmpeg.exe/ffprobe.exe and .gitkeep placeholders).
Demucs settings can be tweaked at the top of main.py:
DEMUCS_MODEL- model name (defaulthtdemucs_ft)DEMUCS_SHIFTS- number of random shifts for prediction averaging (default2)DEMUCS_OVERLAP- overlap between prediction windows (default0.5)
stem-splitter.ini, next to main.py (or next to stem-splitter.exe for
build.bat/bundle.bat/the installer), controls where video\ and audio\
output go:
[paths]
audio_dir = %USERPROFILE%\Music\stem-splitter
video_dir = %USERPROFILE%\Videos\stem-splitterAbsolute paths are used as-is (%VAR% and ~ are expanded); relative paths
are resolved against stem-splitter.ini's own folder. Leave a value blank,
or delete the file, to fall back to the audio\/video\ defaults. Editing
an installed copy's stem-splitter.ini directly is safe - reinstalling or
upgrading via the installer won't overwrite an existing one.