moseca / app /service /demucs_runner.py
Fabio Grasso
init moseca
b1fdcc2
raw
history blame
No virus
6.27 kB
import argparse
import sys
from pathlib import Path
from typing import List
import os
from dora.log import fatal
import torch as th
from demucs.apply import apply_model, BagOfModels
from demucs.audio import save_audio
from demucs.pretrained import get_model_from_args, ModelLoadingError
from demucs.separate import load_track
import streamlit as st
@st.cache_data(show_spinner=False)
def separator(
tracks: List[Path],
out: Path,
model: str,
shifts: int,
overlap: float,
stem: str,
int24: bool,
float32: bool,
clip_mode: str,
mp3: bool,
mp3_bitrate: int,
verbose: bool,
*args,
**kwargs,
):
"""Separate the sources for the given tracks
Args:
tracks (Path): Path to tracks
out (Path): Folder where to put extracted tracks. A subfolder with the model name will be
created.
model (str): Model name
shifts (int): Number of random shifts for equivariant stabilization.
Increase separation time but improves quality for Demucs.
10 was used in the original paper.
overlap (float): Overlap
stem (str): Only separate audio into {STEM} and no_{STEM}.
int24 (bool): Save wav output as 24 bits wav.
float32 (bool): Save wav output as float32 (2x bigger).
clip_mode (str): Strategy for avoiding clipping: rescaling entire signal if necessary
(rescale) or hard clipping (clamp).
mp3 (bool): Convert the output wavs to mp3.
mp3_bitrate (int): Bitrate of converted mp3.
verbose (bool): Verbose
"""
if os.environ.get("LIMIT_CPU", False):
th.set_num_threads(1)
jobs = 1
else:
# Number of jobs. This can increase memory usage but will be much faster when
# multiple cores are available.
jobs = os.cpu_count()
if th.cuda.is_available():
device = "cuda"
else:
device = "cpu"
args = argparse.Namespace()
args.tracks = tracks
args.out = out
args.model = model
args.device = device
args.shifts = shifts
args.overlap = overlap
args.stem = stem
args.int24 = int24
args.float32 = float32
args.clip_mode = clip_mode
args.mp3 = mp3
args.mp3_bitrate = mp3_bitrate
args.jobs = jobs
args.verbose = verbose
args.filename = "{track}/{stem}.{ext}"
args.split = True
args.segment = None
args.name = model
args.repo = None
try:
model = get_model_from_args(args)
except ModelLoadingError as error:
fatal(error.args[0])
if args.segment is not None and args.segment < 8:
fatal("Segment must greater than 8. ")
if ".." in args.filename.replace("\\", "/").split("/"):
fatal('".." must not appear in filename. ')
if isinstance(model, BagOfModels):
print(
f"Selected model is a bag of {len(model.models)} models. "
"You will see that many progress bars per track."
)
if args.segment is not None:
for sub in model.models:
sub.segment = args.segment
else:
if args.segment is not None:
model.segment = args.segment
model.cpu()
model.eval()
if args.stem is not None and args.stem not in model.sources:
fatal(
'error: stem "{stem}" is not in selected model. STEM must be one of {sources}.'.format(
stem=args.stem, sources=", ".join(model.sources)
)
)
out = args.out / args.name
out.mkdir(parents=True, exist_ok=True)
print(f"Separated tracks will be stored in {out.resolve()}")
for track in args.tracks:
if not track.exists():
print(
f"File {track} does not exist. If the path contains spaces, "
'please try again after surrounding the entire path with quotes "".',
file=sys.stderr,
)
continue
print(f"Separating track {track}")
wav = load_track(track, model.audio_channels, model.samplerate)
ref = wav.mean(0)
wav = (wav - ref.mean()) / ref.std()
sources = apply_model(
model,
wav[None],
device=args.device,
shifts=args.shifts,
split=args.split,
overlap=args.overlap,
progress=True,
num_workers=args.jobs,
)[0]
sources = sources * ref.std() + ref.mean()
if args.mp3:
ext = "mp3"
else:
ext = "wav"
kwargs = {
"samplerate": model.samplerate,
"bitrate": args.mp3_bitrate,
"clip": args.clip_mode,
"as_float": args.float32,
"bits_per_sample": 24 if args.int24 else 16,
}
if args.stem is None:
for source, name in zip(sources, model.sources):
stem = out / args.filename.format(
track=track.name.rsplit(".", 1)[0],
trackext=track.name.rsplit(".", 1)[-1],
stem=name,
ext=ext,
)
stem.parent.mkdir(parents=True, exist_ok=True)
save_audio(source, str(stem), **kwargs)
else:
sources = list(sources)
stem = out / args.filename.format(
track=track.name.rsplit(".", 1)[0],
trackext=track.name.rsplit(".", 1)[-1],
stem=args.stem,
ext=ext,
)
stem.parent.mkdir(parents=True, exist_ok=True)
save_audio(sources.pop(model.sources.index(args.stem)), str(stem), **kwargs)
# Warning : after poping the stem, selected stem is no longer in the list 'sources'
other_stem = th.zeros_like(sources[0])
for i in sources:
other_stem += i
stem = out / args.filename.format(
track=track.name.rsplit(".", 1)[0],
trackext=track.name.rsplit(".", 1)[-1],
stem="no_" + args.stem,
ext=ext,
)
stem.parent.mkdir(parents=True, exist_ok=True)
save_audio(other_stem, str(stem), **kwargs)