diff --git a/CHANGELOG.md b/CHANGELOG.md index 912f906..95a9af6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +**Long 4K renders and disk space, canvas density, a voice padded to 8 bits, and qa warns about a stale mix** +- Why: lessons from a 457 s HyperFrames canvas film with narration. Its 4K render refused to start (about 57 GB of temporary frames, 53 GB free); a per-frame `setTransform(1, 0, 0, 1, 0, 0)` dropped the 2× scale; padding the voice with `anullsrc` and the `concat` filter quantised it to 8 bits (2,000+ qa click warnings); `bin/vh mix` stopped on a `ding` given a `pitch`, and `bin/vh qa` then reported on the previous mix.wav without a hint. +- engines/README "出 4K": at 4K every capture is a screenshot (drawElement and Linux BeginFrame capture do not supersample), and multi-worker screenshot capture stores every frame as a JPEG beside the output first, an estimated 4 MB a frame; HyperFrames 0.8.82 refuses to start when its estimate is over 90 % of that disk's free space, and checks again from the first 10 frames' real sizes. Bitrate and render time vary a lot with the picture (this canvas film: about 26 Mbps and 1.4 s per second of film; the 3D intro film: about 125 Mbps and 5–8 s), so the "re-encode 4K over about 135 s" line holds for the intro film only. `HF_CAPTURE_PARALLEL_STREAM=true` streams the frames into the encoder (the 13,710 frames took about 11 minutes with 4 workers on an M3 Max), or `--workers 1`; `NODE_OPTIONS=--max-old-space-size=8192` is HyperFrames' own advice when its worker count may outgrow the heap. In the table: reset a canvas transform with `setTransform(dpr, 0, 0, dpr, 0, 0)`, not `(1, 0, 0, 1, 0, 0)` or `resetTransform()`; a WebGL layer drawn into the canvas sizes its canvas and viewport by the same dpr and computes positions in CSS pixels rather than from `gl_FragCoord`. +- `bin/vh qa` warns, never fails, when the mix may be stale: an input written more than 2 s after it (events.json, the beat map, `--events`, `--voice`, `--timeline`, `--words`, the inputs the stems' meta.json names; `qa mix` compares them with meta.json and checks only this), an SFX event landing after its end, or narration ending after it. The warning heads the report and rides on the last line, which the showcase and swatch scripts print. +- tools/audio/README: `t` is where a built-in lands (riser, swish_rev and tape end on it, so a riser starts `dur` earlier); which shaping keys each built-in takes (the four signals take none, nor a `variant`); `bin/vh mix` writes nothing when it stops on an error. Padding a voice with `-f lavfi -i anullsrc` and the `concat` filter can make it 8-bit: the lavfi input decodes as `pcm_u8`, and with the silence first (its length set by `d=` or an `atrim`) concat converts the voice to u8; neither a `pcm_s24le` output nor an `aformat` after concat helps (ffmpeg 8.0.1; silence last, or an input `-t`, happened to stay exact). Use `bin/vh tts --lead`, `adelay` and `apad`, or numpy; with concat, `aformat=sample_fmts=s32` on every input. `bin/vh tts`'s own gaps are unaffected (checked sample for sample). playbook/04 points to `--lead` (it works without `--beats`; the usage lines of `bin/vh tts` and tts.py no longer nest it under `--beats`, and sfx.py no longer says qa always stops on a wrong shaping key), the riser's landing point and the stale warning. + **`bin/vh textcheck`: canvas text covered by the picture, too faint, or out of the frame** - Why: `npx hyperframes check` measures DOM text only; text a script draws into a `` is pixels to it. A 457 s canvas film's own script measured its 106 reads this way and found 3 with too little contrast that frame review had missed. This makes that check a command any canvas film can run. - A composition opts in with one line, `window.__vh = { draw: (t) => …, texts }` (`texts` optional). Before its scripts run, the check hooks the canvas's `fillText` and `strokeText`, so the film's own text functions stay as they are: kinetic type drawn one character at a time, a line drawn in parts, and a "·" or an arrow drawn as a shape still match the read. diff --git a/bin/vh b/bin/vh index f9241ed..00e9332 100755 --- a/bin/vh +++ b/bin/vh @@ -665,7 +665,7 @@ cmd_check() { [ "$rc" != 1 ] && [ "$fail" = 0 ] # exit 1 on isolated frames, a spec mismatch or a scale FAIL; the other findings are for review } cmd_tts() { - local proj=${1:?usage: bin/vh tts [provider] [voice] [zh|en] [instruct] [--beats map.json --snap beat|half|downbeat --lead s] [--align gemini] [--join block|all] [--resume]} pos=() deps="" + local proj=${1:?usage: bin/vh tts [provider] [voice] [zh|en] [instruct] [--beats map.json --snap beat|half|downbeat] [--lead s] [--align gemini] [--join block|all] [--resume]} pos=() deps="" shift while [ $# -gt 0 ] && [ ${#pos[@]} -lt 4 ] && [[ "$1" != --* ]]; do pos+=("$1"); shift; done # positionals stop at the first --flag local prov=${pos[0]:-qwen} voice=${pos[1]:-} lang=${pos[2]:-} instruct=${pos[3]:-} a prev="" diff --git a/engines/README.md b/engines/README.md index b4775eb..7d43806 100644 --- a/engines/README.md +++ b/engines/README.md @@ -56,9 +56,10 @@ HYPERFRAMES_RENDER_DETACHED=1 nohup caffeinate -i npx hyperframes render --quali | 自己就对,不用改代码 | 要写代码 | |---|---| -| DOM 文字、CSS 的线和边框、SVG | 2D ``:宽高乘 `devicePixelRatio`,CSS 尺寸仍是合成尺寸,再 `ctx.scale(dpr, dpr)`,之后坐标照旧按 1080p 写(只开大画布、不 scale,画面会挤在左上角四分之一) | +| DOM 文字、CSS 的线和边框、SVG | 2D ``:宽高乘 `devicePixelRatio`,CSS 尺寸仍是合成尺寸,再 `ctx.scale(dpr, dpr)`,之后坐标照旧按 1080p 写(只开大画布、不 scale,画面会挤在左上角四分之一)。每帧开头重置变换也要带上 dpr:`ctx.setTransform(dpr, 0, 0, dpr, 0, 0)`;写成 `setTransform(1, 0, 0, 1, 0, 0)` 或 `resetTransform()` 会清掉这个 2 倍,1080p 下看不出来,4K 下又挤回左上角(清屏、按设备像素整幅贴一张离屏层时可以先用单位矩阵,画完再设回 dpr) | | `ctx.scale` 之后画的 canvas 内容,包括 `lineWidth`、`measureText` | 拿 `canvas.width`、`image.width`、`getImageData` 去算布局的地方:这些是物理像素,要除以 dpr | | 4K 的 `