Skip to content

t27f R4b.1: beat graph -- three scenes crossfaded at the beat clock's cuts, on whole frames (Closes #10603) - #10605

Closed
gHashTag wants to merge 4 commits into
masterfrom
claude/t27f-beat-graph
Closed

gHashTag wants to merge 4 commits into
masterfrom
claude/t27f-beat-graph

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

Closes #10603. Part R4b.1 of epic #10560. This PR is stacked on #10598 (R4a, the beat clock), which should merge first; until then this diff also shows #10598's two commits.

What

specs/t27f/lesson_reel.t27 turns the beat clock's cuts into video:

  • beat_xfade_frame(c) gives the base frame where the crossfade for a cut at c ms starts: the last frame at or before c - BEAT_HALF_MS. Both cuts round the same way, so neither takes a frame from the other beat.
  • beat_solo_frames(b, f0, f1) gives how many frames beat b is on screen alone. It is a floor.
  • beat_graph and the new LR_MODE=4:
    • Each input is set to fps=30 and settb=AVTB.
    • Then xfade=transition=fade:duration=0.500:offset=beat_frame_ms(f), twice.
    • Then trim=end_frame=450.
    • Like mode 3, it refuses a voice that does not fit.

In the AV_TIME_BASE timebase the offset is not rounded a second time. xfade starts on the first frame at or after the offset, so beat_frame_ms(f) selects frame f.

Tests

On the Railway lab: 17/17 pass, 0 vacuous. The seal is saved and verified. The t27b ledger row is unchanged (blocked, type [N]T).

test asserts
every cut from 1250 to 13500 ms lands on the last frame at or before half a crossfade ahead 24504
the offset printed for every frame 0..450 selects that frame and no earlier one 1352
control: an offset rounded up to the next ms misses its frame on 300 of 451 frames 1
on R4a's voice grid, every lesson in both languages, every beat keeps MIN_SCENE_FRAMES whole frames, except the counted early case 18902

Measured with ffmpeg

ffmpeg 5.1.9 on the Railway lab. Inputs 0..2 were solid red, green and blue, 15 s at 30 fps. The table gives each run of identical frames, from od | uniq -c of the raw RGB output.

voice (ms) offsets (s) frames red xfade green xfade blue
1300 / 4200 / 6800 (lesson 1) 1.666, 13.233 450 50 15 332 15 38
2500 / 8000 / 9000 2.900, 11.233 450 88 14 235 15 98
200 / 1500 / 13100 (early case) 0.933, 2.433 450 28 15 30 15 362
9000 / 9000 / 9000 refused
  • Every solid run equals beat_solo_frames.
  • Where the offset falls on a whole ms (2.900 s is frame 87 exactly), the first transition frame is still pure red, so that run is one frame longer.
  • In the early case the open beat has 28 frames, under a scene's 30. This is the case R4a gives way on, and the grid test counts it.

Not claimed

🤖 Generated with Claude Code

gHashTag and others added 4 commits October 19, 2026 01:33
…s, crossfaded (Closes #10596)

R4a of epic #10560. lesson_reel.t27 gains a beat clock: cut 0 at the goal
phrase's start (held to a 1250 ms open floor), cut 1 at cta_from, each a
crossfade of reel.t27's TRANSITION_FRAMES centred on the cut. Seven tests:
frame constants, lesson 1's measured cuts, the short-title floor, a grid of
80 voices x every lesson x both languages (full clock, voice too short for an
open scene, refused), weights summing to 1000 at every 10 ms, exactly two
beat changes over 450 frames, and a fixed-thirds control that lands
mid-phrase.

The clock lives in lesson_reel.t27, not in the beat_clock.t27 module the
issue names: importing phrase_start or cta_from into another module leaves
them UNRESOLVED because token_floor_mux.t27 declares the same names, and
the splicer drops ambiguous imports even on an explicit path (#10556).

Lab: 13/13 tests pass, 0 vacuous; seal saved and verified. t27b still
blocked on type [N]T, as the ledger row already says; max_not_pass unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eel (Refs #10596)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ock's cuts, on whole frames (Closes #10603)

lesson_reel.t27 gains:
- beat_xfade_frame: the last base frame at or before c - BEAT_HALF_MS.
- beat_solo_frames: a floor on how many frames each beat shows alone.
- beat_graph: inputs 0..2 (the open, main and close scene clips), crossfaded with xfade at
  offset beat_frame_ms(f) in AV_TIME_BASE, then trimmed to 450 frames.
- LR_MODE=4, which prints the graph and refuses a voice that does not fit, as mode 3 does.

Tests: 17/17 pass, 0 vacuous.
- New: every cut from 1250 to 13500 ms lands on its frame.
- New: the offset for every frame 0..450 selects that frame.
- New control: an offset rounded up misses 300 of 451 frames.
- New: on R4a's voice grid, every beat keeps MIN_SCENE_FRAMES whole frames, except the counted
  early case.

Measured with ffmpeg 5.1 on the Railway lab, on solid-colour inputs, 450 frames each:
- 1300/4200/6800 ms: runs 50 / 332 / 38.
- 2500/8000/9000 ms: runs 88 / 235 / 98.
- 200/1500/13100 ms: runs 28 / 30 / 362.
- 9000/9000/9000 ms: refused.

Every run equals beat_solo_frames, with one extra frame when the offset falls on a whole ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r runs (Refs #10603)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-10-18 19:52:56 UTC

Summary

Status Count
Total Open PRs 50
PRs with Failing Checks 43
PRs with All Checks Green 7
READY 1
FAILING 43
PENDING 0
NO CHECKS YET 0

These columns do not partition: 1 + 43 + 0 + 0 = 44, and there are 50 open PRs. A PR is being counted twice or not at all.

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=fad12492937a != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

@gHashTag

Copy link
Copy Markdown
Owner Author

Carried by #10627, which contains this branch's commits merged with master (the master merge resolves the conflict #10598's squash-merge left here). Merging #10627 lands this work too; this PR can then be closed.

@gHashTag

Copy link
Copy Markdown
Owner Author

Carried by #10627. That PR's branch holds every commit of this one, and it is mergeable with master: master was merged into it in 919c636e8 and the seals were re-verified on the Railway lab.

This PR is not getting its own master merge. The pre-commit foreign-budget hook refuses any merge that brings in master's #10587 lines (cli/tri/src/main.rs); see #10414. Merging #10627 lands this work. After that, this PR can be closed with no diff left.

@gHashTag

Copy link
Copy Markdown
Owner Author

Landed on master through #10627 (squash d61e981), whose branch contains this PR's head commit; its issue is closed. Closing as carried.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

t27f R4b.1: beat graph -- the reel's three scenes crossfaded at the beat clock's cuts, on whole frames

1 participant