Commit
·
5ae6d86
1
Parent(s):
aa3931e
another failure at getting rid of these silence dips. reverting
Browse files- jam_worker.py +38 -98
jam_worker.py
CHANGED
|
@@ -117,8 +117,6 @@ class JamWorker(threading.Thread):
|
|
| 117 |
self._spool = np.zeros((0, 2), dtype=np.float32) # (S,2) target SR
|
| 118 |
self._spool_written = 0 # absolute frames written into spool
|
| 119 |
|
| 120 |
-
self._pending_overlap_model = None
|
| 121 |
-
|
| 122 |
# bar clock: start with offset 0; if you have a downbeat estimator, set base later
|
| 123 |
self._bar_clock = BarClock(self.params.target_sr, self.params.bpm, self.params.beats_per_bar, base_offset_samples=0)
|
| 124 |
|
|
@@ -422,106 +420,48 @@ class JamWorker(threading.Thread):
|
|
| 422 |
|
| 423 |
# ---------- core streaming helpers ----------
|
| 424 |
|
| 425 |
-
def _append_model_chunk_and_spool(self, wav: au.Waveform)
|
| 426 |
-
"""
|
| 427 |
-
|
| 428 |
-
We hold the previous tail (model-rate), mix it with the current head (equal-power),
|
| 429 |
-
append that mixed overlap to the spool, then append the current body.
|
| 430 |
-
The current tail is kept pending for the next call.
|
| 431 |
-
"""
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
# ---- unpack model-rate samples ----
|
| 435 |
s = wav.samples.astype(np.float32, copy=False)
|
| 436 |
if s.ndim == 1:
|
| 437 |
-
s = s[:, None]
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
# No crossfade configured -> just emit whole thing
|
| 453 |
-
y = (s if self._rs is None else self._rs.process(s, final=False))
|
| 454 |
-
self._spool = np.concatenate([self._spool, y], axis=0) if self._spool.size else y
|
| 455 |
-
self._spool_written += y.shape[0]
|
| 456 |
-
# For continuity tracking of model stream:
|
| 457 |
-
self._model_stream = s if self._model_stream is None else np.concatenate([self._model_stream, s], axis=0)
|
| 458 |
-
return
|
| 459 |
-
|
| 460 |
-
# If the chunk is too short to hold a full overlap, accumulate into pending and wait
|
| 461 |
-
if n_samps <= xfade_n:
|
| 462 |
-
tail = s # keep most recent xfade_n samples
|
| 463 |
-
self._pending_overlap_model = tail if self._pending_overlap_model is None \
|
| 464 |
-
else np.concatenate([self._pending_overlap_model, tail], axis=0)[-xfade_n:]
|
| 465 |
-
# Keep model stream continuity too
|
| 466 |
-
self._model_stream = s if self._model_stream is None else np.concatenate([self._model_stream, s], axis=0)
|
| 467 |
-
return
|
| 468 |
-
|
| 469 |
-
# ---- split current chunk into head / body / tail at model rate ----
|
| 470 |
-
head = s[:xfade_n, :]
|
| 471 |
-
body = s[xfade_n:-xfade_n, :] if n_samps >= (2 * xfade_n) else None
|
| 472 |
-
tail = s[-xfade_n:, :]
|
| 473 |
-
|
| 474 |
-
# === First call path (no previous tail to mix) ===
|
| 475 |
-
if self._pending_overlap_model is None or self._pending_overlap_model.shape[0] != xfade_n:
|
| 476 |
-
# Model-stream continuity (drop preroll like before, so future mixes line up)
|
| 477 |
-
new_part_for_stream = s[xfade_n:] if xfade_n < n_samps else s[:0]
|
| 478 |
-
self._model_stream = new_part_for_stream.copy() if self._model_stream is None \
|
| 479 |
-
else np.concatenate([self._model_stream, new_part_for_stream], axis=0)
|
| 480 |
-
|
| 481 |
-
# Emit only the body (if present); DO NOT emit tail yet
|
| 482 |
-
if body is not None and body.size:
|
| 483 |
-
y_body = body if self._rs is None else self._rs.process(body, final=False)
|
| 484 |
-
if y_body.size:
|
| 485 |
-
self._spool = np.concatenate([self._spool, y_body], axis=0) if self._spool.size else y_body
|
| 486 |
-
self._spool_written += y_body.shape[0]
|
| 487 |
-
|
| 488 |
-
# Hold tail for next head
|
| 489 |
-
self._pending_overlap_model = tail.copy()
|
| 490 |
return
|
| 491 |
|
| 492 |
-
#
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
| 497 |
-
|
| 498 |
-
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
|
| 511 |
-
|
| 512 |
-
if y_mixed.size:
|
| 513 |
-
self._spool = np.concatenate([self._spool, y_mixed], axis=0) if self._spool.size else y_mixed
|
| 514 |
-
self._spool_written += y_mixed.shape[0]
|
| 515 |
-
|
| 516 |
-
if body is not None and body.size:
|
| 517 |
-
y_body = body if self._rs is None else self._rs.process(body, final=False)
|
| 518 |
-
if y_body.size:
|
| 519 |
-
self._spool = np.concatenate([self._spool, y_body], axis=0)
|
| 520 |
-
self._spool_written += y_body.shape[0]
|
| 521 |
-
|
| 522 |
-
# Keep the new tail for next time
|
| 523 |
-
self._pending_overlap_model = tail.copy()
|
| 524 |
-
|
| 525 |
|
| 526 |
def _should_generate_next_chunk(self) -> bool:
|
| 527 |
# Allow running ahead relative to whichever is larger: last *consumed*
|
|
|
|
| 117 |
self._spool = np.zeros((0, 2), dtype=np.float32) # (S,2) target SR
|
| 118 |
self._spool_written = 0 # absolute frames written into spool
|
| 119 |
|
|
|
|
|
|
|
| 120 |
# bar clock: start with offset 0; if you have a downbeat estimator, set base later
|
| 121 |
self._bar_clock = BarClock(self.params.target_sr, self.params.bpm, self.params.beats_per_bar, base_offset_samples=0)
|
| 122 |
|
|
|
|
| 420 |
|
| 421 |
# ---------- core streaming helpers ----------
|
| 422 |
|
| 423 |
+
def _append_model_chunk_and_spool(self, wav: au.Waveform):
|
| 424 |
+
"""Crossfade into the model-rate stream and write the *non-overlapped*
|
| 425 |
+
tail to the target-SR spool."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 426 |
s = wav.samples.astype(np.float32, copy=False)
|
| 427 |
if s.ndim == 1:
|
| 428 |
+
s = s[:, None]
|
| 429 |
+
sr = self._model_sr
|
| 430 |
+
xfade_s = float(self.mrt.config.crossfade_length)
|
| 431 |
+
xfade_n = int(round(max(0.0, xfade_s) * sr))
|
| 432 |
+
|
| 433 |
+
if self._model_stream is None:
|
| 434 |
+
# first chunk: drop the preroll (xfade) then spool
|
| 435 |
+
new_part = s[xfade_n:] if xfade_n < s.shape[0] else s[:0]
|
| 436 |
+
self._model_stream = new_part.copy()
|
| 437 |
+
if new_part.size:
|
| 438 |
+
y = (new_part.astype(np.float32, copy=False)
|
| 439 |
+
if self._rs is None else
|
| 440 |
+
self._rs.process(new_part.astype(np.float32, copy=False), final=False))
|
| 441 |
+
self._spool = np.concatenate([self._spool, y], axis=0)
|
| 442 |
+
self._spool_written += y.shape[0]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 443 |
return
|
| 444 |
|
| 445 |
+
# crossfade into existing stream
|
| 446 |
+
if xfade_n > 0 and self._model_stream.shape[0] >= xfade_n and s.shape[0] >= xfade_n:
|
| 447 |
+
tail = self._model_stream[-xfade_n:]
|
| 448 |
+
head = s[:xfade_n]
|
| 449 |
+
t = np.linspace(0, np.pi/2, xfade_n, endpoint=False, dtype=np.float32)[:, None]
|
| 450 |
+
mixed = tail * np.cos(t) + head * np.sin(t)
|
| 451 |
+
self._model_stream = np.concatenate([self._model_stream[:-xfade_n], mixed, s[xfade_n:]], axis=0)
|
| 452 |
+
new_part = s[xfade_n:]
|
| 453 |
+
else:
|
| 454 |
+
self._model_stream = np.concatenate([self._model_stream, s], axis=0)
|
| 455 |
+
new_part = s
|
| 456 |
+
|
| 457 |
+
# spool only the *new* non-overlapped part
|
| 458 |
+
if new_part.size:
|
| 459 |
+
y = (new_part.astype(np.float32, copy=False)
|
| 460 |
+
if self._rs is None else
|
| 461 |
+
self._rs.process(new_part.astype(np.float32, copy=False), final=False))
|
| 462 |
+
if y.size:
|
| 463 |
+
self._spool = np.concatenate([self._spool, y], axis=0)
|
| 464 |
+
self._spool_written += y.shape[0]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 465 |
|
| 466 |
def _should_generate_next_chunk(self) -> bool:
|
| 467 |
# Allow running ahead relative to whichever is larger: last *consumed*
|