Skip to content

Commit b4a2902

Browse files
authored
Merge pull request #4 from SUC-DriverOld/main
Add process callback and manage process bar
2 parents a1500dd + c0acacd commit b4a2902

5 files changed

Lines changed: 134 additions & 38 deletions

File tree

pymss/modules/vocal_remover/common_separator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def __init__(self, config):
3535
self.model_path = config.get("model_path")
3636
self.model_data = config.get("model_data")
3737
self.sample_rate = config.get("sample_rate")
38-
self.callback = config.get("callback", None)
38+
self.progress_callback = config.get("progress_callback", None)
3939

4040
self.primary_stem_name = self.model_data.get("primary_stem", "primary_stem")
4141
self.secondary_stem_name = self.model_data.get("secondary_stem", "secondary_stem")

pymss/modules/vocal_remover/vr_separator.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,8 @@ def execute(x_mag_pad, roi_size):
271271
write_pos = 0
272272
batch_starts = range(0, patches, self.batch_size)
273273
process_batches = tqdm(batch_starts, leave=False, desc="Processing VR batches") if self.debug else batch_starts
274+
if self.progress_callback:
275+
self.progress_callback(0, patches, "Processing VR batches")
274276
if self._use_mlx_full_forward(device):
275277
import mlx.core as mx
276278

@@ -280,8 +282,8 @@ def execute(x_mag_pad, roi_size):
280282
pred = pred.astype(mx.float32).transpose(1, 2, 0, 3).reshape(pred.shape[1], pred.shape[2], -1)
281283
mask_batches.append(pred)
282284
write_pos += pred.shape[2]
283-
if self.callback:
284-
self.callback["progress"] = min(0.99 * (i / patches), 0.99)
285+
if self.progress_callback:
286+
self.progress_callback(min(i + self.batch_size, patches), patches, "Processing VR batches")
285287
return mx.concatenate(mask_batches, axis=2)[:, :, :write_pos]
286288

287289
with torch.inference_mode():
@@ -303,8 +305,8 @@ def execute(x_mag_pad, roi_size):
303305
)
304306
mask[:, :, write_pos:write_pos + pred.size(2)] = pred
305307
write_pos += pred.size(2)
306-
if self.callback:
307-
self.callback["progress"] = min(0.99 * (i / patches), 0.99)
308+
if self.progress_callback:
309+
self.progress_callback(min(i + self.batch_size, patches), patches, "Processing VR batches")
308310
return mask[:, :, :write_pos]
309311

310312
def adjust_aggr_torch(mask, is_non_accom_stem):

pymss/separator.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ def __init__(
362362
audio_params = {"wav_bit_depth": "FLOAT", "flac_bit_depth": "PCM_24", "mp3_bit_rate": "320k", "m4a_bit_rate": "192k", "m4a_aac_at_quality": 2},
363363
logger = None,
364364
debug = False,
365+
progress_callback = None,
365366
inference_params = {
366367
"batch_size": None,
367368
"overlap_size": None,
@@ -390,6 +391,7 @@ def __init__(
390391
self.audio_params = audio_params
391392
self.logger = logger
392393
self.debug = debug
394+
self.progress_callback = progress_callback
393395
self.inference_params = inference_params
394396

395397
if self.debug:
@@ -497,7 +499,7 @@ def load_model(self):
497499
"model_path": self.model_path,
498500
"model_data": model_data,
499501
"sample_rate": 44100,
500-
"callback": None,
502+
"progress_callback": self.progress_callback,
501503
}
502504
model = VRSeparator(common_config, config.inference)
503505
model.load_model()
@@ -745,7 +747,16 @@ def _separate(self, mix, pbar, stems=None):
745747
mix_orig = mix.copy()
746748
mix, norm_stats = _normalize_mix(mix, self.config.inference.get('normalize', False), self.logger)
747749
full_result = [
748-
demix(self.config, self.model, track, self.device, pbar=pbar, model_type=self.model_type, source_indices=source_indices)
750+
demix(
751+
self.config,
752+
self.model,
753+
track,
754+
self.device,
755+
pbar=pbar,
756+
model_type=self.model_type,
757+
source_indices=source_indices,
758+
progress_callback=self.progress_callback,
759+
)
749760
for track in _tta_variants(mix, self.use_tta, self.logger)
750761
]
751762

pymss/utils.py

Lines changed: 113 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,45 @@
1010
from .config import load_config
1111

1212

13+
class _ProgressContext:
14+
def __init__(self, pbar=False, total=1, callback=None, done=0, message="Processing audio chunks"):
15+
self.enabled = bool(pbar or callback)
16+
self.bar = None
17+
self.callback = callback
18+
self.done = done
19+
self.total = total
20+
self.message = message
21+
if not self.enabled:
22+
return
23+
self.bar = tqdm(total=total, desc=message, leave=False) if pbar else None
24+
self.total = int(self.total or 1)
25+
self.done = int(self.done or 0)
26+
self.emit()
27+
28+
def emit(self, done=None):
29+
if not self.enabled:
30+
return
31+
if done is not None:
32+
self.done = int(done)
33+
if self.callback is None:
34+
return
35+
self.callback(min(self.done, self.total), self.total, self.message)
36+
37+
def update(self, amount):
38+
if not self.enabled:
39+
return
40+
amount = int(amount)
41+
if self.bar:
42+
self.bar.update(amount)
43+
self.done += amount
44+
self.emit()
45+
46+
def close(self):
47+
if not self.enabled:
48+
return
49+
if self.bar:
50+
self.bar.close()
51+
1352
def get_model_from_config(model_type, config_path, model_kwargs_override=None):
1453
model_kwargs_override = model_kwargs_override or {}
1554
config = load_config(config_path)
@@ -247,7 +286,18 @@ def _add_weighted_chunk(result, counter, chunk, window, start, length):
247286
counter[..., start:start + length] += window
248287

249288

250-
def _run_complete_chunks(model, mix, windows, result, counter, chunk_size, step, batch_size, progress_bar, source_indices=None):
289+
def _run_complete_chunks(
290+
model,
291+
mix,
292+
windows,
293+
result,
294+
counter,
295+
chunk_size,
296+
step,
297+
batch_size,
298+
progress,
299+
source_indices=None,
300+
):
251301
n_chunks = _complete_chunk_count(mix.shape[1], chunk_size, step)
252302
if n_chunks == 0:
253303
return 0
@@ -272,13 +322,25 @@ def _run_complete_chunks(model, mix, windows, result, counter, chunk_size, step,
272322
step,
273323
start_offset=batch_start * step,
274324
)
275-
if progress_bar:
276-
progress_bar.update(step * (batch_end - batch_start))
325+
progress.update(step * (batch_end - batch_start))
277326

278327
return n_complete
279328

280329

281-
def _run_tail_chunks(model, mix, starts, windows, result, counter, chunk_size, step, batch_size, first_chunk, progress_bar, source_indices=None):
330+
def _run_tail_chunks(
331+
model,
332+
mix,
333+
starts,
334+
windows,
335+
result,
336+
counter,
337+
chunk_size,
338+
step,
339+
batch_size,
340+
first_chunk,
341+
progress,
342+
source_indices=None,
343+
):
282344
for batch_start in range(first_chunk, len(starts), batch_size):
283345
batch_indices = range(batch_start, min(batch_start + batch_size, len(starts)))
284346
batch = [(_extract_chunk(mix, starts[idx], chunk_size), idx) for idx in batch_indices]
@@ -288,8 +350,7 @@ def _run_tail_chunks(model, mix, starts, windows, result, counter, chunk_size, s
288350
start = starts[idx]
289351
_add_weighted_chunk(result, counter, chunks[j], windows[idx], start, length)
290352

291-
if progress_bar:
292-
progress_bar.update(step * len(batch_data))
353+
progress.update(step * len(batch_data))
293354

294355

295356
def _finalize_overlap(result, counter, length_init, border):
@@ -449,7 +510,7 @@ def _can_demix_mlx_full(model, device):
449510
)
450511

451512

452-
def demix_track_mlx_full(config, model, mix, device, pbar=False, source_indices=None):
513+
def demix_track_mlx_full(config, model, mix, device, pbar=False, source_indices=None, progress_callback=None):
453514
import mlx.core as mx
454515

455516
C = config.audio.chunk_size
@@ -463,7 +524,7 @@ def demix_track_mlx_full(config, model, mix, device, pbar=False, source_indices=
463524
starts, windows = _mlx_build_chunk_plan(mix.shape[1], C, step, fade_size)
464525
result = mx.zeros((_source_count(config, source_indices), mix.shape[0], mix.shape[1]), dtype=mx.float32)
465526
counter = mx.zeros((1, 1, mix.shape[1]), dtype=mx.float32)
466-
progress_bar = tqdm(total=mix.shape[1], desc="Processing audio chunks", leave=False) if pbar else None
527+
progress = _ProgressContext(pbar, mix.shape[1], progress_callback)
467528

468529
for batch_start in range(0, len(starts), batch_size):
469530
batch_indices = range(batch_start, min(batch_start + batch_size, len(starts)))
@@ -473,18 +534,17 @@ def demix_track_mlx_full(config, model, mix, device, pbar=False, source_indices=
473534
for j, ((_, length), idx) in enumerate(batch):
474535
result, counter = _mlx_add_weighted_chunk(result, counter, chunks[j], windows[idx], starts[idx], length)
475536
mx.eval(result, counter)
476-
if progress_bar:
477-
progress_bar.update(step * len(batch))
537+
progress.update(step * len(batch))
478538

479-
if progress_bar:
480-
progress_bar.close()
539+
progress.close()
540+
progress.emit(mix.shape[1])
481541
return _sources_to_dict(config, _mlx_finalize_overlap(result, counter, length_init, border), source_indices)
482542

483543

484544
demix_track_mlx_roformer = demix_track_mlx_full
485545

486546

487-
def demix_track(config, model, mix, device, pbar=False, source_indices=None):
547+
def demix_track(config, model, mix, device, pbar=False, source_indices=None, progress_callback=None):
488548
C = config.audio.chunk_size
489549
source_indices = _normalize_source_indices(config, source_indices)
490550
step = _get_inference_step(config, C)
@@ -501,32 +561,51 @@ def demix_track(config, model, mix, device, pbar=False, source_indices=None):
501561
with _autocast(device, config.training.get('use_amp', True)):
502562
with torch.inference_mode():
503563
result, counter = _init_overlap_buffers(config, mix, device, use_complete_fast_path, source_indices)
504-
progress_bar = tqdm(total=mix.shape[1], desc="Processing audio chunks", leave=False) if pbar else None
564+
progress = _ProgressContext(pbar, mix.shape[1], progress_callback)
505565

506566
with _model_source_context(model, source_indices):
507567
complete_chunks = 0
508568
if use_complete_fast_path:
509569
complete_chunks = _run_complete_chunks(
510-
model, mix_device, chunk_windows, result, counter, C, step, batch_size, progress_bar, source_indices
570+
model,
571+
mix_device,
572+
chunk_windows,
573+
result,
574+
counter,
575+
C,
576+
step,
577+
batch_size,
578+
progress,
579+
source_indices,
511580
)
512581

513582
_run_tail_chunks(
514-
model, mix_device, chunk_starts, chunk_windows, result, counter, C, step, batch_size,
515-
complete_chunks, progress_bar, source_indices
583+
model,
584+
mix_device,
585+
chunk_starts,
586+
chunk_windows,
587+
result,
588+
counter,
589+
C,
590+
step,
591+
batch_size,
592+
complete_chunks,
593+
progress,
594+
source_indices,
516595
)
596+
progress.emit(mix.shape[1])
517597

518598

519-
if progress_bar:
520-
progress_bar.close()
599+
progress.close()
521600

522601
estimated_sources = _finalize_overlap(result, counter, length_init, border)
523602

524603
return _sources_to_dict(config, estimated_sources, source_indices)
525604

526605

527-
def demix_track_demucs(config, model, mix, device, pbar=False, source_indices=None):
606+
def demix_track_demucs(config, model, mix, device, pbar=False, source_indices=None, progress_callback=None):
528607
if _can_demix_mlx_full(model, device):
529-
return demix_track_mlx_full(config, model, mix.cpu().numpy(), device, pbar=pbar, source_indices=source_indices)
608+
return demix_track_mlx_full(config, model, mix.cpu().numpy(), device, pbar=pbar, source_indices=source_indices, progress_callback=progress_callback)
530609

531610
source_indices = _normalize_source_indices(config, source_indices)
532611
source_names = _source_names(config)
@@ -543,7 +622,7 @@ def demix_track_demucs(config, model, mix, device, pbar=False, source_indices=No
543622
i = 0
544623
batch_data = []
545624
batch_locations = []
546-
progress_bar = tqdm(total=mix.shape[1], desc="Processing audio chunks", leave=False) if pbar else None
625+
progress = _ProgressContext(pbar, mix.shape[1], progress_callback)
547626

548627
while i < mix.shape[1]:
549628
part = mix[:, i:i + C].to(device)
@@ -563,11 +642,12 @@ def demix_track_demucs(config, model, mix, device, pbar=False, source_indices=No
563642
counter[..., start:start+l] += 1.
564643
batch_data, batch_locations = [], []
565644

566-
if progress_bar:
567-
progress_bar.update(step)
645+
if progress.bar:
646+
progress.bar.update(step)
647+
progress.emit(min(i, mix.shape[1]))
568648

569-
if progress_bar:
570-
progress_bar.close()
649+
progress.close()
650+
progress.emit(mix.shape[1])
571651

572652
estimated_sources = (result / counter).cpu().numpy()
573653
np.nan_to_num(estimated_sources, copy=False, nan=0.0)
@@ -576,13 +656,15 @@ def demix_track_demucs(config, model, mix, device, pbar=False, source_indices=No
576656
return estimated_sources
577657
return _sources_to_dict(config, estimated_sources, source_indices)
578658

579-
def demix(config, model, mix: NDArray, device, pbar=False, model_type: str = None, source_indices=None) -> Dict[str, NDArray]:
659+
def demix(config, model, mix: NDArray, device, pbar=False, model_type: str = None, source_indices=None, progress_callback=None) -> Dict[str, NDArray]:
580660
if _can_demix_mlx_full(model, device):
581-
return demix_track_mlx_full(config, model, mix, device, pbar=pbar, source_indices=source_indices)
661+
return demix_track_mlx_full(config, model, mix, device, pbar=pbar, source_indices=source_indices, progress_callback=progress_callback)
582662
mix = torch.tensor(mix, dtype=torch.float32)
583663
if model_type in {'demucs', 'tasnet', 'legacy_demucs', 'legacy_tasnet'}:
584664
from .modules.legacy_demucs import apply_legacy_model
585665

666+
progress = _ProgressContext(callback=progress_callback)
667+
progress.emit(0)
586668
with _autocast(device, config.training.get('use_amp', True)):
587669
with torch.inference_mode():
588670
estimates = apply_legacy_model(
@@ -593,7 +675,8 @@ def demix(config, model, mix: NDArray, device, pbar=False, model_type: str = Non
593675
overlap=float(config.inference.get('overlap', 0.25)),
594676
progress=pbar,
595677
).cpu().numpy()
678+
progress.emit(1)
596679
return dict(zip(config.training.instruments, estimates))
597680
if model_type == 'htdemucs':
598-
return demix_track_demucs(config, model, mix, device, pbar=pbar, source_indices=source_indices)
599-
return demix_track(config, model, mix, device, pbar=pbar, source_indices=source_indices)
681+
return demix_track_demucs(config, model, mix, device, pbar=pbar, source_indices=source_indices, progress_callback=progress_callback)
682+
return demix_track(config, model, mix, device, pbar=pbar, source_indices=source_indices, progress_callback=progress_callback)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "pymss"
7-
version = "2.0.4"
7+
version = "2.0.5"
88
description = "Python package for music source separation."
99
readme = { file = "README.md", content-type = "text/markdown" }
1010
requires-python = ">=3.10"

0 commit comments

Comments
 (0)