Commit
·
579e8a2
1
Parent(s):
17c0977
reverting again...we keep making it worse
Browse files- jam_worker.py +36 -94
jam_worker.py
CHANGED
@@ -117,9 +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_tail_model = None # type: Optional[np.ndarray]
|
121 |
-
|
122 |
-
|
123 |
# bar clock: start with offset 0; if you have a downbeat estimator, set base later
|
124 |
self._bar_clock = BarClock(self.params.target_sr, self.params.bpm, self.params.beats_per_bar, base_offset_samples=0)
|
125 |
|
@@ -423,103 +420,48 @@ class JamWorker(threading.Thread):
|
|
423 |
|
424 |
# ---------- core streaming helpers ----------
|
425 |
|
426 |
-
def _append_model_chunk_and_spool(self, wav: au.Waveform)
|
427 |
-
"""
|
428 |
-
|
429 |
-
overlap once the next head arrives. Then emit the new body+tail as usual.
|
430 |
-
|
431 |
-
Keeps the externally visible timing identical to the original pipeline while fixing
|
432 |
-
the audible boundary.
|
433 |
-
"""
|
434 |
-
import numpy as np
|
435 |
-
|
436 |
-
# ---------- unpack model-rate samples ----------
|
437 |
s = wav.samples.astype(np.float32, copy=False)
|
438 |
if s.ndim == 1:
|
439 |
s = s[:, None]
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
return y if self._rs is None else self._rs.process(y, final=False)
|
455 |
-
|
456 |
-
# trivial cases
|
457 |
-
if n_samps == 0:
|
458 |
-
return
|
459 |
-
if xfade_n <= 0 or n_samps < (xfade_n + 1):
|
460 |
-
# No crossfade or too short to hold a head
|
461 |
-
y_all = _rs_to_target(s)
|
462 |
-
if y_all.size:
|
463 |
-
self._spool = np.concatenate([self._spool, y_all], axis=0) if self._spool.size else y_all
|
464 |
-
self._spool_written += y_all.shape[0]
|
465 |
-
# keep model stream contiguous for internal consumers
|
466 |
-
self._model_stream = s if self._model_stream is None else np.concatenate([self._model_stream, s], axis=0)
|
467 |
return
|
468 |
|
469 |
-
#
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
# equal-power mix at model rate
|
479 |
-
t = np.linspace(0.0, np.pi / 2.0, xfade_n, endpoint=False, dtype=np.float32)[:, None]
|
480 |
-
cosw = np.cos(t, dtype=np.float32)
|
481 |
-
sinw = np.sin(t, dtype=np.float32)
|
482 |
-
mixed_model = (prev_tail * cosw) + (head * sinw) # [xfade_n, C]
|
483 |
-
|
484 |
-
# resample the MIXED overlap to target SR
|
485 |
-
y_mixed = _rs_to_target(mixed_model.astype(np.float32))
|
486 |
-
|
487 |
-
# compute 'target_xfade_n' as the length of y_mixed (robust to resampler latency)
|
488 |
-
target_xfade_n = int(y_mixed.shape[0])
|
489 |
-
|
490 |
-
# pop last target_xfade_n samples from the spool (they currently hold the old tail)
|
491 |
-
if target_xfade_n > 0 and self._spool.size and self._spool.shape[0] >= target_xfade_n:
|
492 |
-
self._spool = self._spool[:-target_xfade_n, :]
|
493 |
-
self._spool_written -= target_xfade_n
|
494 |
-
|
495 |
-
# append the corrected mixed overlap
|
496 |
-
if y_mixed.size:
|
497 |
-
self._spool = np.concatenate([self._spool, y_mixed], axis=0) if self._spool.size else y_mixed
|
498 |
-
self._spool_written += y_mixed.shape[0]
|
499 |
-
|
500 |
-
# ----- (B) Emit this chunk's body and tail at target SR (same as the original behavior) -----
|
501 |
-
if body is not None and body.size:
|
502 |
-
y_body = _rs_to_target(body.astype(np.float32))
|
503 |
-
if y_body.size:
|
504 |
-
self._spool = np.concatenate([self._spool, y_body], axis=0) if self._spool.size else y_body
|
505 |
-
self._spool_written += y_body.shape[0]
|
506 |
-
|
507 |
-
y_tail = _rs_to_target(tail.astype(np.float32))
|
508 |
-
if y_tail.size:
|
509 |
-
self._spool = np.concatenate([self._spool, y_tail], axis=0) if self._spool.size else y_tail
|
510 |
-
self._spool_written += y_tail.shape[0]
|
511 |
-
|
512 |
-
# ----- (C) Maintain model-stream continuity like before -----
|
513 |
-
# (Drop the model preroll on the very first append; otherwise splice with mixed + new content.)
|
514 |
-
if self._model_stream is None or self._model_stream.shape[0] < xfade_n:
|
515 |
-
new_part_for_stream = s[xfade_n:] if xfade_n < n_samps else s[:0]
|
516 |
-
self._model_stream = new_part_for_stream.copy()
|
517 |
else:
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
#
|
522 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
523 |
|
524 |
def _should_generate_next_chunk(self) -> bool:
|
525 |
# 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*
|