Spaces:
Runtime error
Runtime error
Commit
Β·
d96e1a0
1
Parent(s):
86dd29c
fixing logging
Browse files- jam_worker.py +44 -26
jam_worker.py
CHANGED
@@ -552,26 +552,25 @@ class JamWorker(threading.Thread):
|
|
552 |
return self.idx <= (horizon_anchor + self._max_buffer_ahead)
|
553 |
|
554 |
def _emit_ready(self):
|
555 |
-
"""Emit next chunk(s) if the spool has enough samples. With
|
556 |
|
557 |
|
558 |
-
QDB_SILENCE = -55.0
|
559 |
EPS = 1e-12
|
560 |
|
561 |
def rms_dbfs(x: np.ndarray) -> float:
|
562 |
-
# x: float32 [-1,1]; return single-channel RMS dBFS (mean over channels if stereo)
|
563 |
if x.ndim == 2:
|
564 |
x = x.mean(axis=1)
|
565 |
rms = float(np.sqrt(np.mean(np.square(x)) + EPS))
|
566 |
return 20.0 * np.log10(max(rms, EPS))
|
567 |
|
568 |
def qbar_rms_dbfs(x: np.ndarray, seg_len: int) -> list[float]:
|
569 |
-
vals = []
|
570 |
if x.ndim == 2:
|
571 |
mono = x.mean(axis=1)
|
572 |
else:
|
573 |
mono = x
|
574 |
N = mono.shape[0]
|
|
|
575 |
for i in range(0, N, seg_len):
|
576 |
seg = mono[i:min(i + seg_len, N)]
|
577 |
if seg.size == 0:
|
@@ -580,32 +579,51 @@ class JamWorker(threading.Thread):
|
|
580 |
vals.append(20.0 * np.log10(max(r, EPS)))
|
581 |
return vals
|
582 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
583 |
while True:
|
584 |
start, end = self._bar_clock.bounds_for_chunk(self.idx, self.params.bars_per_chunk)
|
585 |
if end > self._spool_written:
|
586 |
-
# Not enough audio buffered for the next full chunk
|
587 |
-
# Debug the readiness gap once per idx
|
588 |
-
# print(f"[emit idx={self.idx}] need end={end}, have={self._spool_written} (Ξ={end - self._spool_written})")
|
589 |
break
|
590 |
|
591 |
-
|
592 |
-
loop = self._spool[start:end] # shape: [samples, channels] @ target_sr
|
593 |
|
594 |
-
# ----
|
595 |
-
spb = self._bar_clock.bar_samps
|
596 |
-
qlen = max(1, spb // 4)
|
597 |
q_rms_pre = qbar_rms_dbfs(loop, qlen)
|
598 |
-
# Mark segments that look like near-silence
|
599 |
silent_marks_pre = ["π’" if v > QDB_SILENCE else "π₯" for v in q_rms_pre[:8]]
|
600 |
-
print(f"[emit idx={self.idx}] pre-LM qRMS dBFS: {
|
601 |
|
602 |
-
# Loudness match
|
603 |
-
|
604 |
if self.params.ref_loop is not None and self.params.loudness_mode != "none":
|
605 |
ref = self.params.ref_loop.as_stereo().resample(self.params.target_sr)
|
606 |
wav = au.Waveform(loop.copy(), int(self.params.target_sr))
|
607 |
try:
|
608 |
-
matched,
|
609 |
ref, wav,
|
610 |
method=self.params.loudness_mode,
|
611 |
headroom_db=self.params.headroom_db
|
@@ -614,13 +632,15 @@ class JamWorker(threading.Thread):
|
|
614 |
except Exception as e:
|
615 |
print(f"[emit idx={self.idx}] loudness-match ERROR: {e}; proceeding with un-matched audio")
|
616 |
|
617 |
-
|
|
|
|
|
618 |
q_rms_post = qbar_rms_dbfs(loop, qlen)
|
619 |
silent_marks_post = ["π’" if v > QDB_SILENCE else "π₯" for v in q_rms_post[:8]]
|
620 |
-
if
|
621 |
-
print(f"[emit idx={self.idx}] post-LM qRMS dBFS: {
|
622 |
else:
|
623 |
-
print(f"[emit idx={self.idx}] post-LM qRMS dBFS: {
|
624 |
|
625 |
# Encode & ship
|
626 |
audio_b64, total_samples, channels = wav_bytes_base64(loop, int(self.params.target_sr))
|
@@ -639,22 +659,19 @@ class JamWorker(threading.Thread):
|
|
639 |
}
|
640 |
chunk = JamChunk(index=self.idx, audio_base64=audio_b64, metadata=meta)
|
641 |
|
642 |
-
# Emit to outbox
|
643 |
with self._cv:
|
644 |
self._outbox[self.idx] = chunk
|
645 |
self._cv.notify_all()
|
646 |
|
647 |
-
# ---- DEBUG: boundary bookkeeping ----
|
648 |
print(f"[emit idx={self.idx}] slice [{start}:{end}] (len={end-start}), spool_written={self._spool_written}")
|
649 |
-
|
650 |
self.idx += 1
|
651 |
|
652 |
-
#
|
653 |
with self._lock:
|
654 |
if self._pending_token_splice is not None:
|
655 |
spliced = self._coerce_tokens(self._pending_token_splice["tokens"])
|
656 |
try:
|
657 |
-
self.state.context_tokens = spliced
|
658 |
self._pending_token_splice = None
|
659 |
print(f"[emit idx={self.idx}] installed token splice (in-place)")
|
660 |
except Exception:
|
@@ -673,6 +690,7 @@ class JamWorker(threading.Thread):
|
|
673 |
self._pending_reseed = None
|
674 |
print(f"[emit idx={self.idx}] performed full reseed")
|
675 |
|
|
|
676 |
# ---------- main loop ----------
|
677 |
|
678 |
def run(self):
|
|
|
552 |
return self.idx <= (horizon_anchor + self._max_buffer_ahead)
|
553 |
|
554 |
def _emit_ready(self):
|
555 |
+
"""Emit next chunk(s) if the spool has enough samples. With robust RMS debug."""
|
556 |
|
557 |
|
558 |
+
QDB_SILENCE = -55.0
|
559 |
EPS = 1e-12
|
560 |
|
561 |
def rms_dbfs(x: np.ndarray) -> float:
|
|
|
562 |
if x.ndim == 2:
|
563 |
x = x.mean(axis=1)
|
564 |
rms = float(np.sqrt(np.mean(np.square(x)) + EPS))
|
565 |
return 20.0 * np.log10(max(rms, EPS))
|
566 |
|
567 |
def qbar_rms_dbfs(x: np.ndarray, seg_len: int) -> list[float]:
|
|
|
568 |
if x.ndim == 2:
|
569 |
mono = x.mean(axis=1)
|
570 |
else:
|
571 |
mono = x
|
572 |
N = mono.shape[0]
|
573 |
+
vals = []
|
574 |
for i in range(0, N, seg_len):
|
575 |
seg = mono[i:min(i + seg_len, N)]
|
576 |
if seg.size == 0:
|
|
|
579 |
vals.append(20.0 * np.log10(max(r, EPS)))
|
580 |
return vals
|
581 |
|
582 |
+
def fmt_db_list(vals):
|
583 |
+
return ['%5.1f' % v for v in vals[:8]]
|
584 |
+
|
585 |
+
def extract_gain_db(g):
|
586 |
+
# Accept float/int, dict{'gain_db': ...}, tuple/list, or None
|
587 |
+
if g is None:
|
588 |
+
return None
|
589 |
+
if isinstance(g, (int, float)):
|
590 |
+
return float(g)
|
591 |
+
if isinstance(g, dict):
|
592 |
+
for k in ('gain_db', 'gain', 'applied_gain_db'):
|
593 |
+
if k in g:
|
594 |
+
try:
|
595 |
+
return float(g[k])
|
596 |
+
except Exception:
|
597 |
+
pass
|
598 |
+
return None
|
599 |
+
if isinstance(g, (list, tuple)) and g:
|
600 |
+
try:
|
601 |
+
return float(g[0])
|
602 |
+
except Exception:
|
603 |
+
return None
|
604 |
+
return None
|
605 |
+
|
606 |
while True:
|
607 |
start, end = self._bar_clock.bounds_for_chunk(self.idx, self.params.bars_per_chunk)
|
608 |
if end > self._spool_written:
|
|
|
|
|
|
|
609 |
break
|
610 |
|
611 |
+
loop = self._spool[start:end]
|
|
|
612 |
|
613 |
+
# ---- pre-LM diagnostics ----
|
614 |
+
spb = self._bar_clock.bar_samps
|
615 |
+
qlen = max(1, spb // 4)
|
616 |
q_rms_pre = qbar_rms_dbfs(loop, qlen)
|
|
|
617 |
silent_marks_pre = ["π’" if v > QDB_SILENCE else "π₯" for v in q_rms_pre[:8]]
|
618 |
+
print(f"[emit idx={self.idx}] pre-LM qRMS dBFS: {fmt_db_list(q_rms_pre)} {''.join(silent_marks_pre)}")
|
619 |
|
620 |
+
# Loudness match (optional)
|
621 |
+
gain_db_applied_raw = None
|
622 |
if self.params.ref_loop is not None and self.params.loudness_mode != "none":
|
623 |
ref = self.params.ref_loop.as_stereo().resample(self.params.target_sr)
|
624 |
wav = au.Waveform(loop.copy(), int(self.params.target_sr))
|
625 |
try:
|
626 |
+
matched, gain_db_applied_raw = match_loudness_to_reference(
|
627 |
ref, wav,
|
628 |
method=self.params.loudness_mode,
|
629 |
headroom_db=self.params.headroom_db
|
|
|
632 |
except Exception as e:
|
633 |
print(f"[emit idx={self.idx}] loudness-match ERROR: {e}; proceeding with un-matched audio")
|
634 |
|
635 |
+
gain_db = extract_gain_db(gain_db_applied_raw)
|
636 |
+
|
637 |
+
# ---- post-LM diagnostics ----
|
638 |
q_rms_post = qbar_rms_dbfs(loop, qlen)
|
639 |
silent_marks_post = ["π’" if v > QDB_SILENCE else "π₯" for v in q_rms_post[:8]]
|
640 |
+
if gain_db is None:
|
641 |
+
print(f"[emit idx={self.idx}] post-LM qRMS dBFS: {fmt_db_list(q_rms_post)} {''.join(silent_marks_post)} (LM: none)")
|
642 |
else:
|
643 |
+
print(f"[emit idx={self.idx}] post-LM qRMS dBFS: {fmt_db_list(q_rms_post)} {''.join(silent_marks_post)} (LM gain {gain_db:+.2f} dB)")
|
644 |
|
645 |
# Encode & ship
|
646 |
audio_b64, total_samples, channels = wav_bytes_base64(loop, int(self.params.target_sr))
|
|
|
659 |
}
|
660 |
chunk = JamChunk(index=self.idx, audio_base64=audio_b64, metadata=meta)
|
661 |
|
|
|
662 |
with self._cv:
|
663 |
self._outbox[self.idx] = chunk
|
664 |
self._cv.notify_all()
|
665 |
|
|
|
666 |
print(f"[emit idx={self.idx}] slice [{start}:{end}] (len={end-start}), spool_written={self._spool_written}")
|
|
|
667 |
self.idx += 1
|
668 |
|
669 |
+
# Apply pending splices/reseeds immediately after a completed emit
|
670 |
with self._lock:
|
671 |
if self._pending_token_splice is not None:
|
672 |
spliced = self._coerce_tokens(self._pending_token_splice["tokens"])
|
673 |
try:
|
674 |
+
self.state.context_tokens = spliced
|
675 |
self._pending_token_splice = None
|
676 |
print(f"[emit idx={self.idx}] installed token splice (in-place)")
|
677 |
except Exception:
|
|
|
690 |
self._pending_reseed = None
|
691 |
print(f"[emit idx={self.idx}] performed full reseed")
|
692 |
|
693 |
+
|
694 |
# ---------- main loop ----------
|
695 |
|
696 |
def run(self):
|