Skip to content

Commit ea922a8

Browse files
author
Zenflow
committed
fix: enforce strict anchor order in step2 — reject match if abs_start < last_matched_abs_end; back-fill reversed with shrinking upper_bound
1 parent 7017949 commit ea922a8

1 file changed

Lines changed: 36 additions & 12 deletions

File tree

scripts/podcast_to_moshi_dataset.py

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -401,8 +401,9 @@ def min_score(n: int) -> float:
401401

402402
# cursor: whisper-word lower-bound for the tight search.
403403
# Never goes backwards on normal matches; back-fill uses explicit lo/hi.
404-
cursor = 0
405-
consec_misses = 0 # consecutive misses on ≥MIN_WORDS segments
404+
cursor = 0
405+
consec_misses = 0 # consecutive misses on ≥MIN_WORDS segments
406+
last_matched_abs_end = 0 # abs index past end of last confirmed match
406407

407408
seg_raw_start = [None] * len(transcript_segs)
408409
seg_raw_end = [None] * len(transcript_segs)
@@ -454,6 +455,7 @@ def min_score(n: int) -> float:
454455
offset = new_offset
455456
new_cursor_t = whisper_words[abs_end - 1]["end"] - OVERLAP_SEC
456457
cursor = max(cursor, bisect.bisect_left(w_starts, new_cursor_t))
458+
last_matched_abs_end = max(last_matched_abs_end, abs_end)
457459
consec_misses = 0
458460

459461
# ── Queue for back-fill ───────────────────────────────────────────────
@@ -468,12 +470,24 @@ def min_score(n: int) -> float:
468470
continue
469471

470472
# ── Successful match ──────────────────────────────────────────────────
473+
# Enforce strict order: abs_start must be >= last_matched_abs_end.
474+
# The cursor already prevents the search window from rewinding, but
475+
# a repeated phrase can still be found at an earlier whisper position
476+
# than where the previous anchor ended (e.g. 'Ich glaube,' appears 50×).
477+
if abs_start < last_matched_abs_end:
478+
pending_miss.append(si)
479+
n_missed += 1
480+
consec_misses += 1
481+
si += 1
482+
continue
483+
471484
offset = whisper_words[abs_start]["start"] - t_start
472485
consec_misses = 0
473486

474487
seg_raw_start[si] = whisper_words[abs_start]["start"]
475488
seg_raw_end[si] = whisper_words[abs_end - 1]["end"]
476489
n_matched += 1
490+
last_matched_abs_end = abs_end
477491

478492
# Advance cursor to 2s before end of this match
479493
new_cursor_t = whisper_words[abs_end - 1]["end"] - OVERLAP_SEC
@@ -488,9 +502,15 @@ def min_score(n: int) -> float:
488502
print(f" W: {wt[:70]!r}", flush=True)
489503

490504
# ── Back-fill pending missed segments using this anchor ───────────────
491-
# Walk in reverse order (closest to anchor first).
492-
# Each pending seg gets a ±TIGHT_WIN window anchored by current offset,
493-
# capped at abs_start so segment order is preserved.
505+
# Walk REVERSED (closest to anchor → farthest from anchor).
506+
# upper_bound starts at abs_start and moves LEFT as we assign.
507+
# Each back-filled segment must end strictly before upper_bound,
508+
# and upper_bound is updated to pa_s so the next (earlier) segment
509+
# is constrained to be placed before the one just assigned.
510+
# This guarantees the back-filled assignments are in transcript order.
511+
upper_bound = abs_start
512+
bf_results: dict = {} # psi → (pa_s, pa_e)
513+
494514
for psi in reversed(pending_miss):
495515
pseg = transcript_segs[psi]
496516
pt_n = norm_words(pseg["text"])
@@ -500,7 +520,7 @@ def min_score(n: int) -> float:
500520
continue
501521
p_expected = pt_s + offset
502522
p_lo = bisect.bisect_left(w_starts, p_expected - TIGHT_WIN)
503-
p_hi = min(abs_start,
523+
p_hi = min(upper_bound,
504524
bisect.bisect_right(w_starts, p_expected + TIGHT_WIN))
505525
if p_lo >= p_hi:
506526
if DEBUG:
@@ -511,19 +531,23 @@ def min_score(n: int) -> float:
511531
pa_s, pa_e, p_score = search_segment(
512532
pt_n, whisper_words, w_starts,
513533
p_lo, p_center, p_half_win, min_score(pt_len))
514-
if pa_s is not None and pa_e <= abs_start:
515-
seg_raw_start[psi] = whisper_words[pa_s]["start"]
516-
seg_raw_end[psi] = whisper_words[pa_e - 1]["end"]
517-
n_matched += 1
518-
n_missed -= 1
534+
if pa_s is not None and pa_e <= upper_bound:
535+
bf_results[psi] = (pa_s, pa_e)
536+
upper_bound = pa_s # earlier segs must be placed before this
519537
if DEBUG:
520538
print(f" BACKFILL MATCH psi={psi:4d} "
521-
f"t={pt_s:.1f}s raw={seg_raw_start[psi]:.1f}s "
539+
f"t={pt_s:.1f}s raw={whisper_words[pa_s]['start']:.1f}s "
522540
f"score={p_score:.2f}", flush=True)
523541
else:
524542
if DEBUG:
525543
print(f" BACKFILL MISS psi={psi:4d} t={pt_s:.1f}s",
526544
flush=True)
545+
546+
for psi, (pa_s, pa_e) in bf_results.items():
547+
seg_raw_start[psi] = whisper_words[pa_s]["start"]
548+
seg_raw_end[psi] = whisper_words[pa_e - 1]["end"]
549+
n_matched += 1
550+
n_missed -= 1
527551
pending_miss.clear()
528552

529553
si += 1

0 commit comments

Comments
 (0)