Skip to content

Commit d182d42

Browse files
Merge pull request #6 from acolombier/chore/add-ci
Add CI to ensure quality and publish the model
2 parents c08c568 + 2bec973 commit d182d42

23 files changed

Lines changed: 795 additions & 384 deletions

.devcontainer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"image":"mcr.microsoft.com/devcontainers/python:3.13"}

.github/workflows/linter.yml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,23 @@ on:
99
jobs:
1010
build:
1111
runs-on: ubuntu-latest
12-
if: ${{ github.repository == 'facebookresearch/demucs' || github.event_name == 'workflow_dispatch' }}
12+
if: ${{ github.repository == 'mixxxdj/demucs' || github.event_name == 'workflow_dispatch' }}
1313
steps:
14-
- uses: actions/checkout@v2
15-
- uses: actions/setup-python@v2
14+
- uses: actions/checkout@v4
15+
- uses: actions/setup-python@v5
1616
with:
17-
python-version: 3.8
17+
python-version: 3.13
1818

19-
- uses: actions/cache@v2
19+
- uses: actions/cache@v4
2020
with:
2121
path: env
22-
key: env-${{ hashFiles('**/requirements.txt', '.github/workflows/*') }}
22+
key: tests-${{ hashFiles('**/requirements.txt') }}
2323

2424
- name: Install dependencies
2525
run: |
2626
python3 -m venv env
2727
. env/bin/activate
2828
python -m pip install --upgrade pip
29-
pip install -r requirements.txt
3029
pip install '.[dev]'
3130
3231

.github/workflows/release.yml

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
name: release
2+
on:
3+
push:
4+
branches: [ main ]
5+
pull_request:
6+
branches: [ main ]
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: write # Fetch code (actions/checkout), create release
11+
packages: write # Upload and publish packages to GitHub Packages
12+
13+
jobs:
14+
build:
15+
runs-on: ubuntu-latest
16+
if: ${{ github.repository == 'mixxxdj/demucs' || github.event_name == 'workflow_dispatch' }}
17+
steps:
18+
- uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0
21+
- uses: actions/setup-python@v5
22+
with:
23+
python-version: 3.13
24+
25+
- uses: actions/cache@v4
26+
with:
27+
path: env
28+
key: build-${{ hashFiles('**/requirements.txt') }}
29+
30+
- run: |
31+
set -eo pipefail
32+
echo "VERSION=$(git describe --tags --match "v*" --exclude "*-onnxmodel")-onnxmodel" >> "${GITHUB_ENV}"
33+
name: "Prepare"
34+
35+
- run: |
36+
python3 -m venv env
37+
. env/bin/activate
38+
pip install .
39+
pip install onnx onnxruntime
40+
./scripts/convert-pth-to-onnx.py ./output
41+
name: Build
42+
43+
- run: |
44+
if ! gh release view --repo "${{ github.repository }}" "${RELEASE_NAME}" &> /dev/null
45+
then
46+
gh release create --repo "${{ github.repository }}" "${RELEASE_NAME}" \
47+
--generate-notes \
48+
--latest \
49+
--target "${{ github.sha }}"
50+
gh release upload --repo "${{ github.repository }}" "${RELEASE_NAME}" output/htdemucs.onnx
51+
fi
52+
env:
53+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
54+
RELEASE_NAME: ${{ env.VERSION }}
55+
shell: bash
56+
if: ${{ github.event_name == 'push' }}
57+
id: ghrelease
58+
name: "Creates a release in GitHub if it doesn't exist yet"
59+
60+
- name: Upload GitHub Actions artifacts
61+
if: ${{ steps.ghrelease.outcome == 'skipped' }}
62+
uses: actions/upload-artifact@v4
63+
with:
64+
name: htdemucs.onnx
65+
path: |
66+
./output/htdemucs.onnx

.github/workflows/tests.yml

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,17 @@ on:
99
jobs:
1010
build:
1111
runs-on: ubuntu-latest
12-
if: ${{ github.repository == 'facebookresearch/demucs' || github.event_name == 'workflow_dispatch' }}
12+
if: ${{ github.repository == 'mixxxdj/demucs' || github.event_name == 'workflow_dispatch' }}
1313
steps:
14-
- uses: actions/checkout@v2
15-
- uses: actions/setup-python@v2
14+
- uses: actions/checkout@v4
15+
- uses: actions/setup-python@v5
1616
with:
17-
python-version: 3.8
17+
python-version: 3.13
1818

19-
- uses: actions/cache@v2
19+
- uses: actions/cache@v4
2020
with:
2121
path: env
22-
key: env-${{ hashFiles('**/requirements.txt', '.github/workflows/*') }}
22+
key: tests-${{ hashFiles('**/requirements.txt') }}
2323

2424
- name: Install dependencies
2525
run: |
@@ -28,9 +28,15 @@ jobs:
2828
python3 -m venv env
2929
. env/bin/activate
3030
python -m pip install --upgrade pip
31-
pip install -r requirements.txt
31+
pip install '.[dev]'
3232
33-
- name: Run separation test
33+
# FIXME: missing dependencies!
34+
# - name: Run separation test
35+
# run: |
36+
# . env/bin/activate
37+
# make test_eval
38+
39+
- name: Run pytest
3440
run: |
3541
. env/bin/activate
36-
make test_eval
42+
pytest ./tests/

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,6 @@ Session.vim
1414
/misc
1515
/mdx
1616
.mypy_cache
17+
*.onnx
18+
*.ort
19+
*.config

demucs/api.py

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
import subprocess
2424

25-
from . import audio_legacy
25+
from . import audio_legacy # noqa: F401
2626
import torch as th
2727
import torchaudio as ta
2828

@@ -118,9 +118,17 @@ def __init__(
118118
self._name = model
119119
self._repo = repo
120120
self._load_model()
121-
self.update_parameter(device=device, shifts=shifts, overlap=overlap, split=split,
122-
segment=segment, jobs=jobs, progress=progress, callback=callback,
123-
callback_arg=callback_arg)
121+
self.update_parameter(
122+
device=device,
123+
shifts=shifts,
124+
overlap=overlap,
125+
split=split,
126+
segment=segment,
127+
jobs=jobs,
128+
progress=progress,
129+
callback=callback,
130+
callback_arg=callback_arg,
131+
)
124132

125133
def update_parameter(
126134
self,
@@ -131,9 +139,7 @@ def update_parameter(
131139
segment: Optional[Union[int, _NotProvided]] = NotProvided,
132140
jobs: Union[int, _NotProvided] = NotProvided,
133141
progress: Union[bool, _NotProvided] = NotProvided,
134-
callback: Optional[
135-
Union[Callable[[dict], None], _NotProvided]
136-
] = NotProvided,
142+
callback: Optional[Union[Callable[[dict], None], _NotProvided]] = NotProvided,
137143
callback_arg: Optional[Union[dict, _NotProvided]] = NotProvided,
138144
):
139145
"""
@@ -213,8 +219,9 @@ def _load_audio(self, track: Path):
213219
wav = None
214220

215221
try:
216-
wav = AudioFile(track).read(streams=0, samplerate=self._samplerate,
217-
channels=self._audio_channels)
222+
wav = AudioFile(track).read(
223+
streams=0, samplerate=self._samplerate, channels=self._audio_channels
224+
)
218225
except FileNotFoundError:
219226
errors["ffmpeg"] = "FFmpeg is not installed."
220227
except subprocess.CalledProcessError:
@@ -269,20 +276,20 @@ def separate_tensor(
269276
wav -= ref.mean()
270277
wav /= ref.std() + 1e-8
271278
out = apply_model(
272-
self._model,
273-
wav[None],
274-
segment=self._segment,
275-
shifts=self._shifts,
276-
split=self._split,
277-
overlap=self._overlap,
278-
device=self._device,
279-
num_workers=self._jobs,
280-
callback=self._callback,
281-
callback_arg=_replace_dict(
282-
self._callback_arg, ("audio_length", wav.shape[1])
283-
),
284-
progress=self._progress,
285-
)
279+
self._model,
280+
wav[None],
281+
segment=self._segment,
282+
shifts=self._shifts,
283+
split=self._split,
284+
overlap=self._overlap,
285+
device=self._device,
286+
num_workers=self._jobs,
287+
callback=self._callback,
288+
callback_arg=_replace_dict(
289+
self._callback_arg, ("audio_length", wav.shape[1])
290+
),
291+
progress=self._progress,
292+
)
286293
if out is None:
287294
raise KeyboardInterrupt
288295
out *= ref.std() + 1e-8
@@ -336,7 +343,7 @@ def list_models(repo: Optional[Path] = None) -> Dict[str, Dict[str, Union[str, P
336343
"""
337344
model_repo: ModelOnlyRepo
338345
if repo is None:
339-
models = _parse_remote_files(REMOTE_ROOT / 'files.txt')
346+
models = _parse_remote_files(REMOTE_ROOT / "files.txt")
340347
model_repo = RemoteRepo(models)
341348
bag_repo = BagOnlyRepo(REMOTE_ROOT, model_repo)
342349
else:
@@ -363,7 +370,7 @@ def list_models(repo: Optional[Path] = None) -> Dict[str, Dict[str, Union[str, P
363370
split=args.split,
364371
segment=args.segment,
365372
jobs=args.jobs,
366-
callback=print
373+
callback=print,
367374
)
368375
out = args.out / args.name
369376
out.mkdir(parents=True, exist_ok=True)

demucs/apply.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def __init__(self, models: tp.List[Model],
5757
self.audio_channels = first.audio_channels
5858
self.samplerate = first.samplerate
5959
self.sources = first.sources
60-
self.models = nn.ModuleList(models)
60+
self.models = tp.cast(tp.List[Model], nn.ModuleList(models))
6161

6262
if weights is None:
6363
weights = [[1. for _ in first.sources] for _ in models]
@@ -142,7 +142,7 @@ def _replace_dict(_dict: tp.Optional[dict], *subs: tp.Tuple[tp.Hashable, tp.Any]
142142
return _dict
143143

144144

145-
def apply_model(model: tp.Union[BagOfModels, Model],
145+
def apply_model(model: tp.Union[BagOfModels, Demucs, HDemucs, HTDemucs],
146146
mix: tp.Union[th.Tensor, TensorChunk],
147147
shifts: int = 1, split: bool = True,
148148
overlap: float = 0.25, transition_power: float = 1.,

0 commit comments

Comments
 (0)