From 6fd372048249cd4b05454d534a38f583aa51703c Mon Sep 17 00:00:00 2001 From: CarlGao4 Date: Sat, 23 Sep 2023 09:14:27 +0800 Subject: [PATCH 01/14] minor fixes for 4.1.0a1 print out the exception when calling callback ensures all threads can be stopped when interrupting separation add release data for 4.0.1 --- .gitignore | 2 +- demucs/__init__.py | 2 +- demucs/api.py | 2 +- demucs/apply.py | 10 ++++++---- docs/release.md | 2 +- docs/windows.md | 2 +- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 6f73669b..179cf0dd 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,4 @@ Session.vim /trash /misc /mdx -.mypy_cache \ No newline at end of file +.mypy_cache diff --git a/demucs/__init__.py b/demucs/__init__.py index ef5cd6f3..e02c0ada 100644 --- a/demucs/__init__.py +++ b/demucs/__init__.py @@ -4,4 +4,4 @@ # This source code is licensed under the license found in the # LICENSE file in the root directory of this source tree. -__version__ = "4.1.0a1" +__version__ = "4.1.0a2" diff --git a/demucs/api.py b/demucs/api.py index fc254fb2..5958f7d9 100644 --- a/demucs/api.py +++ b/demucs/api.py @@ -195,7 +195,7 @@ def update_parameter( self._jobs = jobs if not isinstance(progress, _NotProvided): self._progress = progress - if not isinstance(callback, _NotProvided) and (callback is None or callable(callback)): + if not isinstance(callback, _NotProvided): self._callback = callback if not isinstance(callback_arg, _NotProvided): self._callback_arg = callback_arg diff --git a/demucs/apply.py b/demucs/apply.py index 180db7fe..ae2272d3 100644 --- a/demucs/apply.py +++ b/demucs/apply.py @@ -10,7 +10,9 @@ from concurrent.futures import ThreadPoolExecutor import copy import random +import sys from threading import Lock +import traceback import typing as tp import torch as th @@ -323,17 +325,17 @@ def apply_model(model: tp.Union[BagOfModels, Model], try: callback(_replace_dict(callback_arg, ("state", "start"))) # type: ignore except KeyboardInterrupt: - raise + return None except Exception: - pass + traceback.print_exc(file=sys.stderr) with th.no_grad(): out = model(padded_mix) with lock: try: callback(_replace_dict(callback_arg, ("state", "end"))) # type: ignore except KeyboardInterrupt: - raise + return None except Exception: - pass + traceback.print_exc(file=sys.stderr) assert isinstance(out, th.Tensor) return center_trim(out, length) diff --git a/docs/release.md b/docs/release.md index 1c8dd537..0aee2f71 100644 --- a/docs/release.md +++ b/docs/release.md @@ -14,7 +14,7 @@ Added `--other-method`: method to get `no_{STEM}`, add up all the other stems (a Added type `HTDemucs` to type alias `AnyModel`. -## V4.0.1a1, TBD +## V4.0.1, 8th of September 2023 **From this version, Python 3.7 is no longer supported. This is not a problem since the latest PyTorch 2.0.0 no longer support it either.** diff --git a/docs/windows.md b/docs/windows.md index a84e89bf..fe92f7c3 100644 --- a/docs/windows.md +++ b/docs/windows.md @@ -20,7 +20,7 @@ Start the Anaconda prompt, and run the following bash ``` conda install -c conda-forge ffmpeg -python.exe -m pip install -U demucs PySoundFile +python.exe -m pip install -U demucs SoundFile ``` ### Upgrade From a295a21a0fa93092ffd6f6eb698df393fd5710fc Mon Sep 17 00:00:00 2001 From: CarlGao4 Date: Sun, 24 Sep 2023 09:37:56 +0800 Subject: [PATCH 02/14] Fix model_idx_in_bag always zero --- demucs/apply.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/demucs/apply.py b/demucs/apply.py index ae2272d3..36af61e5 100644 --- a/demucs/apply.py +++ b/demucs/apply.py @@ -207,16 +207,11 @@ def apply_model(model: tp.Union[BagOfModels, Model], estimates: tp.Union[float, th.Tensor] = 0. totals = [0.] * len(model.sources) callback_arg["models"] = len(model.models) - kwargs["callback"] = ( - ( - lambda d, i=callback_arg["model_idx_in_bag"]: callback( - _replace_dict(d, ("model_idx_in_bag", i)) - ) - ) - if callable(callback) - else None - ) for sub_model, model_weights in zip(model.models, model.weights): + kwargs["callback"] = (( + lambda d, i=callback_arg["model_idx_in_bag"]: callback( + _replace_dict(d, ("model_idx_in_bag", i)))) + ) original_model_device = next(iter(sub_model.parameters())).device sub_model.to(device) @@ -255,8 +250,6 @@ def apply_model(model: tp.Union[BagOfModels, Model], shifted = TensorChunk(padded_mix, offset, length + max_shift - offset) kwargs["callback"] = ( (lambda d, i=shift_idx: callback(_replace_dict(d, ("shift_idx", i)))) - if callable(callback) - else None ) res = apply_model(model, shifted, **kwargs, callback_arg=callback_arg) if res is None: @@ -291,8 +284,7 @@ def apply_model(model: tp.Union[BagOfModels, Model], chunk = TensorChunk(mix, offset, segment_length) future = pool.submit(apply_model, model, chunk, **kwargs, callback_arg=callback_arg, callback=(lambda d, i=offset: - callback(_replace_dict(d, ("segment_offset", i)))) - if callable(callback) else None) + callback(_replace_dict(d, ("segment_offset", i))))) futures.append((future, offset)) offset += segment_length if progress: From 16468a2a56b8787a02dcb89294a80e2db4763db4 Mon Sep 17 00:00:00 2001 From: Weiqi Gao Date: Mon, 25 Sep 2023 01:10:31 +0000 Subject: [PATCH 03/14] fix linter --- demucs/apply.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/demucs/apply.py b/demucs/apply.py index 36af61e5..6a433004 100644 --- a/demucs/apply.py +++ b/demucs/apply.py @@ -200,6 +200,8 @@ def apply_model(model: tp.Union[BagOfModels, Model], } out: tp.Union[float, th.Tensor] res: tp.Union[float, th.Tensor, None] + if callback is None: + callback = lambda _: None if isinstance(model, BagOfModels): # Special treatment for bag of model. # We explicitely apply multiple times `apply_model` so that the random shifts From d77c05d8f665cfbb6afadbd72980bace71767d9b Mon Sep 17 00:00:00 2001 From: CarlGao4 Date: Tue, 26 Sep 2023 18:07:26 +0800 Subject: [PATCH 04/14] Fix can't separate empty audio --- demucs/api.py | 2 ++ demucs/apply.py | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/demucs/api.py b/demucs/api.py index 5958f7d9..44dade9e 100644 --- a/demucs/api.py +++ b/demucs/api.py @@ -264,6 +264,8 @@ def separate_tensor( """ if sr is not None and sr != self.samplerate: wav = convert_audio(wav, sr, self._samplerate, self._audio_channels) + if wav.max() == wav.min(): + return wav, {name: (wav / len(self._model.sources)) for name in self._model.sources} ref = wav.mean(0) wav -= ref.mean() wav /= ref.std() diff --git a/demucs/apply.py b/demucs/apply.py index 6a433004..4e5917b4 100644 --- a/demucs/apply.py +++ b/demucs/apply.py @@ -200,8 +200,6 @@ def apply_model(model: tp.Union[BagOfModels, Model], } out: tp.Union[float, th.Tensor] res: tp.Union[float, th.Tensor, None] - if callback is None: - callback = lambda _: None if isinstance(model, BagOfModels): # Special treatment for bag of model. # We explicitely apply multiple times `apply_model` so that the random shifts @@ -212,7 +210,7 @@ def apply_model(model: tp.Union[BagOfModels, Model], for sub_model, model_weights in zip(model.models, model.weights): kwargs["callback"] = (( lambda d, i=callback_arg["model_idx_in_bag"]: callback( - _replace_dict(d, ("model_idx_in_bag", i)))) + _replace_dict(d, ("model_idx_in_bag", i))) if callback else None) ) original_model_device = next(iter(sub_model.parameters())).device sub_model.to(device) @@ -251,7 +249,8 @@ def apply_model(model: tp.Union[BagOfModels, Model], offset = random.randint(0, max_shift) shifted = TensorChunk(padded_mix, offset, length + max_shift - offset) kwargs["callback"] = ( - (lambda d, i=shift_idx: callback(_replace_dict(d, ("shift_idx", i)))) + (lambda d, i=shift_idx: callback(_replace_dict(d, ("shift_idx", i))) + if callback else None) ) res = apply_model(model, shifted, **kwargs, callback_arg=callback_arg) if res is None: @@ -286,7 +285,8 @@ def apply_model(model: tp.Union[BagOfModels, Model], chunk = TensorChunk(mix, offset, segment_length) future = pool.submit(apply_model, model, chunk, **kwargs, callback_arg=callback_arg, callback=(lambda d, i=offset: - callback(_replace_dict(d, ("segment_offset", i))))) + callback(_replace_dict(d, ("segment_offset", i))) + if callback else None)) futures.append((future, offset)) offset += segment_length if progress: From ced1e89d279a43e7baf683186bb854c229b72b14 Mon Sep 17 00:00:00 2001 From: CarlGao4 Date: Tue, 26 Sep 2023 18:36:24 +0800 Subject: [PATCH 05/14] Calls callback when skipping empty audio --- demucs/api.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/demucs/api.py b/demucs/api.py index 44dade9e..209b69c3 100644 --- a/demucs/api.py +++ b/demucs/api.py @@ -29,7 +29,7 @@ from pathlib import Path from typing import Optional, Callable, Dict, Tuple, Union -from .apply import apply_model, _replace_dict +from .apply import apply_model, _replace_dict, BagOfModels from .audio import AudioFile, convert_audio, save_audio from .pretrained import get_model, _parse_remote_files, REMOTE_ROOT from .repo import RemoteRepo, LocalRepo, ModelOnlyRepo, BagOnlyRepo @@ -265,6 +265,24 @@ def separate_tensor( if sr is not None and sr != self.samplerate: wav = convert_audio(wav, sr, self._samplerate, self._audio_channels) if wav.max() == wav.min(): + try: + self._callback( # type: ignore + _replace_dict( + self._callback_arg, + ("state", "end"), + ("model_idx_in_bag", (len(self._model.models) - 1) + if isinstance(self._model, BagOfModels) else 0), + ("shift_idx", self._shifts - 1), + ("segment_offset", wav.shape[1]), + ("audio_length", wav.shape[1]), + ("models", len(self._model.models) if isinstance(self._model, BagOfModels) + else 1) + ) + ) + except KeyboardInterrupt: + raise + except Exception: + pass return wav, {name: (wav / len(self._model.sources)) for name in self._model.sources} ref = wav.mean(0) wav -= ref.mean() From d7555fd223683827223d7b55243f43b1334b1683 Mon Sep 17 00:00:00 2001 From: Weiqi Gao Date: Fri, 13 Oct 2023 18:33:40 +0800 Subject: [PATCH 06/14] Add description for aborting --- docs/api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api.md b/docs/api.md index e6d9e873..5bf2d749 100644 --- a/docs/api.md +++ b/docs/api.md @@ -79,7 +79,7 @@ progress: If true, show a progress bar. ##### Notes for callback -The function will be called with only one positional parameter whose type is `dict`. The `callback_arg` will be combined with information of current separation progress. The progress information will override the values in `callback_arg` if same key has been used. To abort the separation, raise `KeyboardInterrupt`. +The function will be called with only one positional parameter whose type is `dict`. The `callback_arg` will be combined with information of current separation progress. The progress information will override the values in `callback_arg` if same key has been used. To abort the separation, raise `KeyboardInterrupt`. This will directly make the function return `None` but not stop the program. Progress information contains several keys (These keys will always exist): - `model_idx_in_bag`: The index of the submodel in `BagOfModels`. Starts from 0. @@ -127,7 +127,7 @@ progress: If true, show a progress bar. ##### Notes for callback -The function will be called with only one positional parameter whose type is `dict`. The `callback_arg` will be combined with information of current separation progress. The progress information will override the values in `callback_arg` if same key has been used. To abort the separation, raise `KeyboardInterrupt`. +The function will be called with only one positional parameter whose type is `dict`. The `callback_arg` will be combined with information of current separation progress. The progress information will override the values in `callback_arg` if same key has been used. To abort the separation, raise `KeyboardInterrupt`. This will directly make the function return `None` but not stop the program. Progress information contains several keys (These keys will always exist): - `model_idx_in_bag`: The index of the submodel in `BagOfModels`. Starts from 0. From a43029a86c0047e0d32b32f75b727a51a3b0d9a3 Mon Sep 17 00:00:00 2001 From: Weiqi Gao Date: Fri, 13 Oct 2023 18:35:55 +0800 Subject: [PATCH 07/14] Does not ignore callback exception --- demucs/apply.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/demucs/apply.py b/demucs/apply.py index 4e5917b4..66e9ad76 100644 --- a/demucs/apply.py +++ b/demucs/apply.py @@ -317,19 +317,21 @@ def apply_model(model: tp.Union[BagOfModels, Model], padded_mix = mix.padded(valid_length).to(device) with lock: try: - callback(_replace_dict(callback_arg, ("state", "start"))) # type: ignore + if callback is not None: + callback(_replace_dict(callback_arg, ("state", "start"))) # type: ignore except KeyboardInterrupt: return None except Exception: - traceback.print_exc(file=sys.stderr) + raise with th.no_grad(): out = model(padded_mix) with lock: try: - callback(_replace_dict(callback_arg, ("state", "end"))) # type: ignore + if callback is not None: + callback(_replace_dict(callback_arg, ("state", "end"))) # type: ignore except KeyboardInterrupt: return None except Exception: - traceback.print_exc(file=sys.stderr) + raise assert isinstance(out, th.Tensor) return center_trim(out, length) From d1a98e23ce88472e018a0065a689b30564b3361c Mon Sep 17 00:00:00 2001 From: Weiqi Gao Date: Fri, 13 Oct 2023 19:39:37 +0800 Subject: [PATCH 08/14] Fix linter --- demucs/apply.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/demucs/apply.py b/demucs/apply.py index 66e9ad76..8daeb28a 100644 --- a/demucs/apply.py +++ b/demucs/apply.py @@ -10,9 +10,7 @@ from concurrent.futures import ThreadPoolExecutor import copy import random -import sys from threading import Lock -import traceback import typing as tp import torch as th From 5df7a119877657162d220d85044a96c06b32f3f0 Mon Sep 17 00:00:00 2001 From: Weiqi Gao Date: Mon, 16 Oct 2023 14:57:00 +0000 Subject: [PATCH 09/14] Does not ignore exception --- demucs/api.py | 29 ++++++++++++----------------- demucs/apply.py | 4 ---- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/demucs/api.py b/demucs/api.py index 209b69c3..a01f97dd 100644 --- a/demucs/api.py +++ b/demucs/api.py @@ -265,24 +265,19 @@ def separate_tensor( if sr is not None and sr != self.samplerate: wav = convert_audio(wav, sr, self._samplerate, self._audio_channels) if wav.max() == wav.min(): - try: - self._callback( # type: ignore - _replace_dict( - self._callback_arg, - ("state", "end"), - ("model_idx_in_bag", (len(self._model.models) - 1) - if isinstance(self._model, BagOfModels) else 0), - ("shift_idx", self._shifts - 1), - ("segment_offset", wav.shape[1]), - ("audio_length", wav.shape[1]), - ("models", len(self._model.models) if isinstance(self._model, BagOfModels) - else 1) - ) + self._callback( # type: ignore + _replace_dict( + self._callback_arg, + ("state", "end"), + ("model_idx_in_bag", (len(self._model.models) - 1) + if isinstance(self._model, BagOfModels) else 0), + ("shift_idx", self._shifts - 1), + ("segment_offset", wav.shape[1]), + ("audio_length", wav.shape[1]), + ("models", len(self._model.models) if isinstance(self._model, BagOfModels) + else 1) ) - except KeyboardInterrupt: - raise - except Exception: - pass + ) return wav, {name: (wav / len(self._model.sources)) for name in self._model.sources} ref = wav.mean(0) wav -= ref.mean() diff --git a/demucs/apply.py b/demucs/apply.py index 8daeb28a..af2fb0a5 100644 --- a/demucs/apply.py +++ b/demucs/apply.py @@ -319,8 +319,6 @@ def apply_model(model: tp.Union[BagOfModels, Model], callback(_replace_dict(callback_arg, ("state", "start"))) # type: ignore except KeyboardInterrupt: return None - except Exception: - raise with th.no_grad(): out = model(padded_mix) with lock: @@ -329,7 +327,5 @@ def apply_model(model: tp.Union[BagOfModels, Model], callback(_replace_dict(callback_arg, ("state", "end"))) # type: ignore except KeyboardInterrupt: return None - except Exception: - raise assert isinstance(out, th.Tensor) return center_trim(out, length) From 8cdc821f75328fa51d123ee4076904c257bb82bd Mon Sep 17 00:00:00 2001 From: Weiqi Gao Date: Tue, 14 Nov 2023 07:02:48 +0000 Subject: [PATCH 10/14] Disable torchaudio 2.2+ --- requirements.txt | 2 +- requirements_minimal.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 26342361..294290d3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ openunmix pyyaml submitit torch>=1.8.1 -torchaudio>=0.8 +torchaudio>=0.8,<2.1 tqdm treetable soundfile>=0.10.3;sys_platform=="win32" diff --git a/requirements_minimal.txt b/requirements_minimal.txt index 8c6f1e57..1940bf01 100644 --- a/requirements_minimal.txt +++ b/requirements_minimal.txt @@ -6,5 +6,5 @@ lameenc>=1.2 openunmix pyyaml torch>=1.8.1 -torchaudio>=0.8 +torchaudio>=0.8,<2.1 tqdm From 350745cb79bbd629ca3b96982afe34b349757f19 Mon Sep 17 00:00:00 2001 From: Weiqi Gao Date: Tue, 14 Nov 2023 08:24:39 +0000 Subject: [PATCH 11/14] Uses epsilon to deal with empty audio --- demucs/api.py | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/demucs/api.py b/demucs/api.py index a01f97dd..f5f106d6 100644 --- a/demucs/api.py +++ b/demucs/api.py @@ -264,24 +264,9 @@ def separate_tensor( """ if sr is not None and sr != self.samplerate: wav = convert_audio(wav, sr, self._samplerate, self._audio_channels) - if wav.max() == wav.min(): - self._callback( # type: ignore - _replace_dict( - self._callback_arg, - ("state", "end"), - ("model_idx_in_bag", (len(self._model.models) - 1) - if isinstance(self._model, BagOfModels) else 0), - ("shift_idx", self._shifts - 1), - ("segment_offset", wav.shape[1]), - ("audio_length", wav.shape[1]), - ("models", len(self._model.models) if isinstance(self._model, BagOfModels) - else 1) - ) - ) - return wav, {name: (wav / len(self._model.sources)) for name in self._model.sources} ref = wav.mean(0) wav -= ref.mean() - wav /= ref.std() + wav /= ref.std() + 1e-8 out = apply_model( self._model, wav[None], @@ -299,9 +284,9 @@ def separate_tensor( ) if out is None: raise KeyboardInterrupt - out *= ref.std() + out *= ref.std() + 1e-8 out += ref.mean() - wav *= ref.std() + wav *= ref.std() + 1e-8 wav += ref.mean() return (wav, dict(zip(self._model.sources, out[0]))) From 9608782554b57f402157c0fb023adab28d697812 Mon Sep 17 00:00:00 2001 From: Weiqi Gao Date: Tue, 14 Nov 2023 09:15:14 +0000 Subject: [PATCH 12/14] Reraises exception in callback --- demucs/api.py | 2 +- demucs/apply.py | 31 +++++++++++++------------------ demucs/utils.py | 3 +++ docs/api.md | 4 ++-- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/demucs/api.py b/demucs/api.py index f5f106d6..20079a6b 100644 --- a/demucs/api.py +++ b/demucs/api.py @@ -29,7 +29,7 @@ from pathlib import Path from typing import Optional, Callable, Dict, Tuple, Union -from .apply import apply_model, _replace_dict, BagOfModels +from .apply import apply_model, _replace_dict from .audio import AudioFile, convert_audio, save_audio from .pretrained import get_model, _parse_remote_files, REMOTE_ROOT from .repo import RemoteRepo, LocalRepo, ModelOnlyRepo, BagOnlyRepo diff --git a/demucs/apply.py b/demucs/apply.py index af2fb0a5..428866f6 100644 --- a/demucs/apply.py +++ b/demucs/apply.py @@ -150,7 +150,7 @@ def apply_model(model: tp.Union[BagOfModels, Model], num_workers: int = 0, segment: tp.Optional[float] = None, pool=None, lock=None, callback: tp.Optional[tp.Callable[[dict], None]] = None, - callback_arg: tp.Optional[dict] = None) -> tp.Optional[th.Tensor]: + callback_arg: tp.Optional[dict] = None) -> th.Tensor: """ Apply model to a given mixture. @@ -197,7 +197,7 @@ def apply_model(model: tp.Union[BagOfModels, Model], 'lock': lock, } out: tp.Union[float, th.Tensor] - res: tp.Union[float, th.Tensor, None] + res: tp.Union[float, th.Tensor] if isinstance(model, BagOfModels): # Special treatment for bag of model. # We explicitely apply multiple times `apply_model` so that the random shifts @@ -214,8 +214,6 @@ def apply_model(model: tp.Union[BagOfModels, Model], sub_model.to(device) res = apply_model(sub_model, mix, **kwargs, callback_arg=callback_arg) - if res is None: - return res out = res sub_model.to(original_model_device) for k, inst_weight in enumerate(model_weights): @@ -251,8 +249,6 @@ def apply_model(model: tp.Union[BagOfModels, Model], if callback else None) ) res = apply_model(model, shifted, **kwargs, callback_arg=callback_arg) - if res is None: - return res shifted_out = res out += shifted_out[..., max_shift - offset:] out /= shifts @@ -290,10 +286,7 @@ def apply_model(model: tp.Union[BagOfModels, Model], if progress: futures = tqdm.tqdm(futures, unit_scale=scale, ncols=120, unit='seconds') for future, offset in futures: - chunk_out = future.result() # type: tp.Union[None, th.Tensor] - if chunk_out is None: - pool.shutdown(wait=False, cancel_futures=True) - return chunk_out + chunk_out = future.result() # type: th.Tensor chunk_length = chunk_out.shape[-1] out[..., offset:offset + segment_length] += ( weight[:chunk_length] * chunk_out).to(mix.device) @@ -314,18 +307,20 @@ def apply_model(model: tp.Union[BagOfModels, Model], assert isinstance(mix, TensorChunk) padded_mix = mix.padded(valid_length).to(device) with lock: - try: - if callback is not None: + if callback is not None: + try: callback(_replace_dict(callback_arg, ("state", "start"))) # type: ignore - except KeyboardInterrupt: - return None + except Exception: + pool.shutdown(wait=True, cancel_futures=True) + raise with th.no_grad(): out = model(padded_mix) with lock: - try: - if callback is not None: + if callback is not None: + try: callback(_replace_dict(callback_arg, ("state", "end"))) # type: ignore - except KeyboardInterrupt: - return None + except Exception: + pool.shutdown(wait=True, cancel_futures=True) + raise assert isinstance(out, th.Tensor) return center_trim(out, length) diff --git a/demucs/utils.py b/demucs/utils.py index c80fc129..a3f5993e 100755 --- a/demucs/utils.py +++ b/demucs/utils.py @@ -5,6 +5,7 @@ # LICENSE file in the root directory of this source tree. from collections import defaultdict +from concurrent.futures import CancelledError from contextlib import contextmanager import math import os @@ -129,6 +130,8 @@ def __init__(self, func, _dict, *args, **kwargs): def result(self): if self._dict["run"]: return self.func(*self.args, **self.kwargs) + else: + raise CancelledError() def __init__(self, workers=0): self._dict = {"run": True} diff --git a/docs/api.md b/docs/api.md index 5bf2d749..ab55f922 100644 --- a/docs/api.md +++ b/docs/api.md @@ -79,7 +79,7 @@ progress: If true, show a progress bar. ##### Notes for callback -The function will be called with only one positional parameter whose type is `dict`. The `callback_arg` will be combined with information of current separation progress. The progress information will override the values in `callback_arg` if same key has been used. To abort the separation, raise `KeyboardInterrupt`. This will directly make the function return `None` but not stop the program. +The function will be called with only one positional parameter whose type is `dict`. The `callback_arg` will be combined with information of current separation progress. The progress information will override the values in `callback_arg` if same key has been used. To abort the separation, raise an exception in `callback` which should be handled by yourself if you want your codes continue to function. Progress information contains several keys (These keys will always exist): - `model_idx_in_bag`: The index of the submodel in `BagOfModels`. Starts from 0. @@ -127,7 +127,7 @@ progress: If true, show a progress bar. ##### Notes for callback -The function will be called with only one positional parameter whose type is `dict`. The `callback_arg` will be combined with information of current separation progress. The progress information will override the values in `callback_arg` if same key has been used. To abort the separation, raise `KeyboardInterrupt`. This will directly make the function return `None` but not stop the program. +The function will be called with only one positional parameter whose type is `dict`. The `callback_arg` will be combined with information of current separation progress. The progress information will override the values in `callback_arg` if same key has been used. To abort the separation, raise an exception in `callback` which should be handled by yourself if you want your codes continue to function. Progress information contains several keys (These keys will always exist): - `model_idx_in_bag`: The index of the submodel in `BagOfModels`. Starts from 0. From 07845f4d5e3553cd3028861ee54f43aac74e4755 Mon Sep 17 00:00:00 2001 From: CarlGao4 Date: Tue, 14 Nov 2023 17:53:01 +0800 Subject: [PATCH 13/14] Ensure the pool stops when encountering exception --- demucs/apply.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/demucs/apply.py b/demucs/apply.py index 428866f6..1540f3d4 100644 --- a/demucs/apply.py +++ b/demucs/apply.py @@ -286,7 +286,11 @@ def apply_model(model: tp.Union[BagOfModels, Model], if progress: futures = tqdm.tqdm(futures, unit_scale=scale, ncols=120, unit='seconds') for future, offset in futures: - chunk_out = future.result() # type: th.Tensor + try: + chunk_out = future.result() # type: th.Tensor + except Exception: + pool.shutdown(wait=True, cancel_futures=True) + raise chunk_length = chunk_out.shape[-1] out[..., offset:offset + segment_length] += ( weight[:chunk_length] * chunk_out).to(mix.device) @@ -308,19 +312,11 @@ def apply_model(model: tp.Union[BagOfModels, Model], padded_mix = mix.padded(valid_length).to(device) with lock: if callback is not None: - try: - callback(_replace_dict(callback_arg, ("state", "start"))) # type: ignore - except Exception: - pool.shutdown(wait=True, cancel_futures=True) - raise + callback(_replace_dict(callback_arg, ("state", "start"))) # type: ignore with th.no_grad(): out = model(padded_mix) with lock: if callback is not None: - try: - callback(_replace_dict(callback_arg, ("state", "end"))) # type: ignore - except Exception: - pool.shutdown(wait=True, cancel_futures=True) - raise + callback(_replace_dict(callback_arg, ("state", "end"))) # type: ignore assert isinstance(out, th.Tensor) return center_trim(out, length) From 92ffb457f09eb49fdd3833ea398fe517ac97699a Mon Sep 17 00:00:00 2001 From: Weiqi Gao Date: Tue, 14 Nov 2023 20:03:41 +0800 Subject: [PATCH 14/14] Update windows.md for latest instructions --- docs/windows.md | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/docs/windows.md b/docs/windows.md index fe92f7c3..acc48c70 100644 --- a/docs/windows.md +++ b/docs/windows.md @@ -2,23 +2,30 @@ ## Installation and usage -Parts of the code are untested on Windows (in particular, training a new model). If you don't have much experience with Anaconda, python or the shell, here are more detailed instructions. Note that **Demucs is not supported on 32bits systems** (as Pytorch is not available there). +If you don't have much experience with Anaconda, python or the shell, here are more detailed instructions. Note that **Demucs is not supported on 32bits systems** (as Pytorch is not available there). - First install Anaconda with **Python 3.8** or more recent, which you can find [here][install]. - Start the [Anaconda prompt][prompt]. Then, all commands that follow must be run from this prompt. +
+ I have no coding experience and these are too difficult for me + +> Then a GUI is suitable for you. See [Demucs GUI](https://github.com/CarlGao4/Demucs-Gui) + +
+ ### If you want to use your GPU -If you have graphic cards produced by nVidia with more than 6GiB of memory, you can separate tracks with GPU acceleration. To achieve this, you must install Pytorch with CUDA. If Pytorch was already installed (you already installed Demucs for instance), first run `python.exe -m pip uninstall torch torchaudio`. -Then visit [Pytorch Home Page](https://pytorch.org/get-started/locally/) and follow the guide on it to install with CUDA support. +If you have graphic cards produced by NVIDIA with more than 2GiB of memory, you can separate tracks with GPU acceleration. To achieve this, you must install Pytorch with CUDA. If Pytorch was already installed (you already installed Demucs for instance), first run `python.exe -m pip uninstall torch torchaudio`. +Then visit [Pytorch Home Page](https://pytorch.org/get-started/locally/) and follow the guide on it to install with CUDA support. Please make sure that the version of torchaudio should no greater than 2.1 (which is the latest version when this document is written, but 2.2.0 is sure unsupported) ### Installation Start the Anaconda prompt, and run the following -bash -``` + +```cmd conda install -c conda-forge ffmpeg python.exe -m pip install -U demucs SoundFile ``` @@ -33,9 +40,12 @@ Then to use Demucs, just start the **Anaconda prompt** and run: ``` demucs -d cpu "PATH_TO_AUDIO_FILE_1" ["PATH_TO_AUDIO_FILE_2" ...] ``` -The `"` around the filename are required if the path contains spaces. -The separated files will be under `C:\Users\YOUR_USERNAME\demucs\separated\demucs\`. +The `"` around the filename are required if the path contains spaces. A simple way to input these paths is draging a file from a folder into the terminal. +To find out the separated files, you can run this command and open the folders: +``` +explorer separated +``` ### Separating an entire folder @@ -45,7 +55,6 @@ cd FOLDER for %i in (*.mp3) do (demucs -d cpu "%i") ``` - ## Potential errors If you have an error saying that `mkl_intel_thread.dll` cannot be found, you can try to first run