Skip to content

Commit

Permalink
minor fixes for 4.1.0a1 (#552)
Browse files Browse the repository at this point in the history
* 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

* Fix model_idx_in_bag always zero

* fix linter

* Fix can't separate empty audio

* Calls callback when skipping empty audio

* Add description for aborting

* Does not ignore callback exception

* Fix linter

* Does not ignore exception

* Disable torchaudio 2.2+

* Uses epsilon to deal with empty audio

* Reraises exception in callback

* Ensure the pool stops when encountering exception

* Update windows.md for latest instructions
  • Loading branch information
CarlGao4 authored Nov 16, 2023
1 parent 583db9d commit fcd0600
Show file tree
Hide file tree
Showing 10 changed files with 49 additions and 54 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ Session.vim
/trash
/misc
/mdx
.mypy_cache
.mypy_cache
2 changes: 1 addition & 1 deletion demucs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
8 changes: 4 additions & 4 deletions demucs/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -266,7 +266,7 @@ def separate_tensor(
wav = convert_audio(wav, sr, self._samplerate, self._audio_channels)
ref = wav.mean(0)
wav -= ref.mean()
wav /= ref.std()
wav /= ref.std() + 1e-8
out = apply_model(
self._model,
wav[None],
Expand All @@ -284,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])))

Expand Down
51 changes: 17 additions & 34 deletions demucs/apply.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -197,30 +197,23 @@ 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
# are different for each 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))) if callback else None)
)
original_model_device = next(iter(sub_model.parameters())).device
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):
Expand Down Expand Up @@ -252,13 +245,10 @@ 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))))
if callable(callback)
else None
(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:
return res
shifted_out = res
out += shifted_out[..., max_shift - offset:]
out /= shifts
Expand Down Expand Up @@ -289,17 +279,18 @@ 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)))
if callback else None))
futures.append((future, offset))
offset += segment_length
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
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)
Expand All @@ -320,20 +311,12 @@ 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:
callback(_replace_dict(callback_arg, ("state", "start"))) # type: ignore
except KeyboardInterrupt:
raise
except Exception:
pass
with th.no_grad():
out = model(padded_mix)
with lock:
try:
if callback is not None:
callback(_replace_dict(callback_arg, ("state", "end"))) # type: ignore
except KeyboardInterrupt:
raise
except Exception:
pass
assert isinstance(out, th.Tensor)
return center_trim(out, length)
3 changes: 3 additions & 0 deletions demucs/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down
4 changes: 2 additions & 2 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 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.
Expand Down Expand Up @@ -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 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.
Expand Down
2 changes: 1 addition & 1 deletion docs/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.**

Expand Down
27 changes: 18 additions & 9 deletions docs/windows.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,32 @@

## 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.

<details>
<summary>I have no coding experience and these are too difficult for me</summary>

> Then a GUI is suitable for you. See [Demucs GUI](https://github.com/CarlGao4/Demucs-Gui)
</details>

### 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 PySoundFile
python.exe -m pip install -U demucs SoundFile
```

### Upgrade
Expand All @@ -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

Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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"
2 changes: 1 addition & 1 deletion requirements_minimal.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ lameenc>=1.2
openunmix
pyyaml
torch>=1.8.1
torchaudio>=0.8
torchaudio>=0.8,<2.1
tqdm

0 comments on commit fcd0600

Please sign in to comment.