@docyrus/animaguru 0.16.1 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/NOTICE.md +27 -0
  2. package/README.md +58 -14
  3. package/dist/animaguru.js +2235 -1875
  4. package/package.json +1 -1
  5. package/skills/animaguru/SKILL.md +17 -4
  6. package/skills/animaguru/references/cloud.md +2 -2
  7. package/skills/animaguru-art-motion/LICENSE +21 -0
  8. package/skills/animaguru-art-motion/SKILL.md +35 -0
  9. package/skills/animaguru-art-motion/agents/openai.yaml +4 -0
  10. package/skills/animaguru-art-motion/references/art/art-nouveau.md +27 -0
  11. package/skills/animaguru-art-motion/references/art/bauhaus-geometry.md +27 -0
  12. package/skills/animaguru-art-motion/references/art/blue-period.md +27 -0
  13. package/skills/animaguru-art-motion/references/art/cave-painting.md +27 -0
  14. package/skills/animaguru-art-motion/references/art/cinematic-anime-light.md +27 -0
  15. package/skills/animaguru-art-motion/references/art/constructivist-poster.md +27 -0
  16. package/skills/animaguru-art-motion/references/art/contemporary-flat.md +27 -0
  17. package/skills/animaguru-art-motion/references/art/cosmic-comic.md +27 -0
  18. package/skills/animaguru-art-motion/references/art/cubist-facets.md +27 -0
  19. package/skills/animaguru-art-motion/references/art/dunhuang-mural.md +27 -0
  20. package/skills/animaguru-art-motion/references/art/early-cgi.md +27 -0
  21. package/skills/animaguru-art-motion/references/art/egyptian-wall-painting.md +27 -0
  22. package/skills/animaguru-art-motion/references/art/expressionist-waves.md +27 -0
  23. package/skills/animaguru-art-motion/references/art/flat-outline-cartoon.md +27 -0
  24. package/skills/animaguru-art-motion/references/art/gothic-illumination.md +27 -0
  25. package/skills/animaguru-art-motion/references/art/greek-vase-painting.md +27 -0
  26. package/skills/animaguru-art-motion/references/art/hopper-hard-light.md +27 -0
  27. package/skills/animaguru-art-motion/references/art/impressionist-dabs.md +27 -0
  28. package/skills/animaguru-art-motion/references/art/ink-wash.md +27 -0
  29. package/skills/animaguru-art-motion/references/art/klimt-gold.md +27 -0
  30. package/skills/animaguru-art-motion/references/art/matisse-cut-paper.md +27 -0
  31. package/skills/animaguru-art-motion/references/art/monet-water-garden.md +27 -0
  32. package/skills/animaguru-art-motion/references/art/pixel-art.md +27 -0
  33. package/skills/animaguru-art-motion/references/art/polka-dot-infinity.md +27 -0
  34. package/skills/animaguru-art-motion/references/art/pop-print.md +27 -0
  35. package/skills/animaguru-art-motion/references/art/post-impressionist-flow.md +27 -0
  36. package/skills/animaguru-art-motion/references/art/radiant-line-figures.md +27 -0
  37. package/skills/animaguru-art-motion/references/art/rembrandt-chiaroscuro.md +27 -0
  38. package/skills/animaguru-art-motion/references/art/renaissance-sfumato.md +27 -0
  39. package/skills/animaguru-art-motion/references/art/roman-mosaic.md +27 -0
  40. package/skills/animaguru-art-motion/references/art/rubber-hose.md +27 -0
  41. package/skills/animaguru-art-motion/references/art/seurat-pointillism.md +27 -0
  42. package/skills/animaguru-art-motion/references/art/shadow-puppet.md +27 -0
  43. package/skills/animaguru-art-motion/references/art/surrealist-soft-forms.md +27 -0
  44. package/skills/animaguru-art-motion/references/art/vaporwave-vhs.md +27 -0
  45. package/skills/animaguru-art-motion/references/art/watercolor-cel-world.md +27 -0
  46. package/skills/animaguru-art-motion/references/art-styles.md +44 -0
  47. package/skills/animaguru-art-motion/references/coverage.json +190 -0
  48. package/skills/animaguru-art-motion/references/explainer-grammars.md +17 -0
  49. package/skills/animaguru-art-motion/references/explainers/concept-morph.md +21 -0
  50. package/skills/animaguru-art-motion/references/explainers/editorial-evidence.md +21 -0
  51. package/skills/animaguru-art-motion/references/explainers/honest-data-chart.md +21 -0
  52. package/skills/animaguru-art-motion/references/explainers/kinetic-type.md +21 -0
  53. package/skills/animaguru-art-motion/references/explainers/luminous-science.md +21 -0
  54. package/skills/animaguru-art-motion/references/explainers/presenter-explainer.md +21 -0
  55. package/skills/animaguru-art-motion/references/explainers/product-interface.md +21 -0
  56. package/skills/animaguru-art-motion/references/explainers/storytime-acting.md +21 -0
  57. package/skills/animaguru-art-motion/references/explainers/whiteboard-reasoning.md +21 -0
  58. package/skills/animaguru-art-motion/references/production.md +78 -0
  59. package/skills/animaguru-art-motion/references/rendering.md +84 -0
  60. package/skills/animaguru-art-motion/references/sources.md +40 -0
  61. package/skills/animaguru-video/SKILL.md +4 -2
  62. package/templates/video/AGENTS.md +1 -1
  63. package/templates/video/README.md +1 -1
  64. package/templates/video/scripts/audio-mix.mjs +3 -2
  65. package/templates/video/scripts/audio-vo.mjs +1 -1
@@ -0,0 +1,84 @@
1
+ # Rendering, camera and review
2
+
3
+ Contents: runtime mapping; material pipelines; characters and contact; camera; deterministic rendering; review and delivery.
4
+
5
+ ## Map techniques to the installed runtime
6
+
7
+ The upstream source uses Canvas globals such as `PAINT`, `CAM`, `MO`, `DG`, `CL`, `CH`, `UI` and `TOON`. Those are provenance, not dependencies of this skill. Do not import them into AnimaGuru by name or claim their demos are locally installed.
8
+
9
+ In an existing AnimaGuru film, inspect `src/kit/index.ts` or workspace `runtime/kit/index.ts`, the film entry point and local skills. The current kit provides `FilmBase`, time windows, easing, seeded `rng`, text, panels, line reveals and asset helpers. Scene hooks register updates driven by absolute time. Use existing helpers before adding a small scene-local implementation. For Pixi code, read the available `pixijs` skill and relevant assets, container, graphics, sprite, text or filter references.
10
+
11
+ Keep the normal player contract: asynchronous seeking and frame rendering, film dimensions/fps/duration, local media, synchronized captions, audio and resource metadata. A hybrid SVG overlay outside the captured Pixi canvas may disappear from MP4 export; put intended film content inside the supported capture surface or explicitly rasterize/composite it. The source's standalone SVG demo is not compatible with a Canvas-only recorder without adaptation.
12
+
13
+ ## Material pipeline
14
+
15
+ A useful default for painted scenes is:
16
+
17
+ 1. Establish flat form, composition and value.
18
+ 2. Repaint large regions with the selected material renderer.
19
+ 3. Render small props separately with finer parameters.
20
+ 4. Apply compatible material to subjects, preserving silhouettes and features.
21
+ 5. Add motivated light, local motion and restored visible detail.
22
+
23
+ Change that order when the medium demands it. Ink starts with unpainted paper and adds strokes. Pattern art starts with a flat shape and its pattern. Chiaroscuro begins with a readable lit scene and multiplies a light map. Cut paper needs unified silhouettes and contact shadows. A universal filter on a completed flat scene rarely supplies those construction differences.
24
+
25
+ For brush and dot work, define a color group per region. Stable sampled colors with deliberate warm/cool relations work better than independent RGB jitter. Keep stroke positions fixed and change a flow field continuously; re-randomizing the whole screen produces flashing. Line boiling or a stepped pose cadence can be intentional in a local region. Distress, flakes and heavy dirt should usually protect focal faces and exact text.
26
+
27
+ Cache static surfaces, tessera layouts, patterns, masks and blur passes; prewarm heavy caches before capture. Reuse offscreens and reset their drawing state each frame. If reading pixels often, choose a read-friendly offscreen context. Write raw pixel data into an offscreen and composite it through the current camera/mask rather than bypassing them with `putImageData` on the final surface.
28
+
29
+ Keep compositing operations local. Repeated `source-in` can erase earlier patterns: build the pattern layer first and mask it once. `destination-out` on the final surface creates transparent holes; carve isolated pieces before compositing. A paper-color layer applied source-atop can wash a figure white; clip the texture and choose the actual intended blend. When a texture/brush pass excludes a rectangular prop region, it leaves a visible patch; draw that prop as a separate layer instead.
30
+
31
+ Check contour occlusion by part order. A final outline pass across every body part can draw hidden limbs through torsos. Either draw fill/texture/visible outline per part in depth order or derive a proper visible-line mask. Rounded caps and opposite path winding can leave holes at joints; union silhouettes before outlining when needed.
32
+
33
+ ## Characters and contact
34
+
35
+ Measure landmarks from the actual art rather than guessing a generic skeleton. Preserve crop offset, alpha bounds, head/neck anchors, hands, feet and prop contact. Use the same transform for fill, material and outline. Squash and stretch around a support point; a center pivot makes the feet slide.
36
+
37
+ Use actual sprite-sheet rectangles, cadence and anchors from downloaded metadata. Do not assume every generated pose has identical height or padding. Normalize apparent height with measured feet/head landmarks, not destructive anatomical stretching. Do not mirror an asymmetric costume, prop or face detail merely to change travel direction. For walking, a genuine gait and distance-based phase are preferable to sliding a standing sprite.
38
+
39
+ For a hand meeting a world-space prop, transform the contact into the local rig coordinate space. Keep upper/forearm lengths and bend direction stable if using two-bone IK. A generated full-body frame enlarged for a face close-up can lose quality; prepare a matching bust/detail asset. Mouth activity should stop in silence. RMS-driven mouth opening is a coarse performance technique, not verified phoneme lip sync.
40
+
41
+ ## Camera language
42
+
43
+ First identify what the viewer should inspect and whether it is already readable. If so, holding is appropriate. Fit the target's actual bounds with a margin; do not tune arbitrary coordinates until the words nearly fit. Keep neighboring half-cut labels outside the final frame.
44
+
45
+ | Action | Purpose | Useful starting range |
46
+ |---|---|---|
47
+ | Hold | Read, watch an existing action, or let a reaction land | Follow the voice and reading need. |
48
+ | Push | Make a specific detail readable | .6–1.5 s, eased. |
49
+ | Pull back | Restore context or reveal the whole relation | .8–2 s, a controlled long tail. |
50
+ | Pan/glide | Find the next object in one continuous world | .5–.9 s; longer for following a connecting line. |
51
+ | Fast push or slam | A reveal, reversal, key number or joke | Roughly .17–.35 s, followed by a hold. |
52
+ | Hard cut/jump-in | Change viewpoint or land a reaction | Immediate, with matched geometry when it is the same world. |
53
+ | Drift | Sustained atmosphere or an expressive music passage | Use only when the composition and mood benefit. |
54
+
55
+ These are source-derived design suggestions, not quotas. Do not fill every pause with a slow zoom or every noun with a fast one. Once pushed in, consider a pullback, sideways move or held cut. Source footage may already supply sufficient motion. A still image cannot prove camera smoothness; watch at normal speed.
56
+
57
+ For scale travel, use `z = exp(lerp(log(z0), log(z1), easedProgress))`. Continue rendering the live world through the transition rather than enlarging a low-resolution still. Preserve color/position landmarks at the cut, and make the mask or portal cover the complete frame. Use a larger world, background fill and blur overscan to prevent exposed edges. Keep captions, progress and persistent HUD in screen space when they should not move with the scene.
58
+
59
+ ## Deterministic capture
60
+
61
+ Derive every state from absolute film time and stable IDs/seeds. Incremental particle integration, uncontrolled video clocks and render-call-order RNG make random seeks unreliable. Derive particle positions analytically, or use a reproducible simulation with deterministic checkpoints. Seed textures once; derive time-varying variation from explicit quantized time where required.
62
+
63
+ Compare a cold render of time A with warm/seek-order renders A → B → A. Inspect the actual raster output as well as state. Ensure caches include any inputs that alter their output, and invalidate them when those inputs change. Missing scenes, failed imports or syntax errors should fail visibly rather than silently produce a placeholder.
64
+
65
+ The upstream SVG example obtained equal screenshots under a specific Chromium configuration that disabled partial rasterization. Do not generalize that result to every browser. Verify the AnimaGuru capture surface and browser configuration actually used. Await decoded video frames and texture uploads before a frame render resolves.
66
+
67
+ Profile end-to-end capture, including GPU completion when relevant. Upstream milliseconds are measurements of another engine, machine and resolution. They are not guarantees. Optimize heavy work only after measuring the new film: cache static rendering, batch patterns and dots, and reduce unnecessary full-screen passes.
68
+
69
+ ## Review and delivery
70
+
71
+ In the installed starter, use its build, `review`, `frames`, HTML5 and MP4 tools as documented by `animaguru-video`. For a multi-animation workspace, run from the workspace root with the selected animation argument. Keep the film's real resources and clip timing in the resource registry so the timeline and review tools inspect the finished film.
72
+
73
+ Review representative full-size stills, transition midpoints, extreme expressions and the smallest intended display. Watch the complete film at normal speed and listen to the final mix when those capabilities are available. Check:
74
+
75
+ - Does the selected material read immediately, including on the subjects?
76
+ - Does each visible event explain or express something, with natural reading holds?
77
+ - Are exact text, numbers, sources, labels and captions correct and unobscured?
78
+ - Are contact points, layer order, frame edges and scene continuity sound?
79
+ - Do cue timing, mouths, impacts and the actual recording agree?
80
+ - Are arbitrary seeks stable and exports complete at the requested dimensions, duration and frame count?
81
+
82
+ Motion-area, static-frame and jump statistics are diagnostics. A noisy VHS frame can score as moving while nothing meaningful happens; storytime and mathematical explanations may intentionally contain long static holds. Inspect suspicious intervals rather than enforcing a universal threshold or speeding up scenes to pass a score. Do not claim an independent review when none occurred.
83
+
84
+ Deliver the requested local playable film and/or export with production sources. Check that portable HTML plays from local packaged assets, including under a nested URL and iframe where relevant. For MP4 verify codecs, audio, dimensions, duration and exact frame count using the existing exporter. State any unperformed playback/listening checks. Publishing, uploading and package release are separate actions from local film creation.
@@ -0,0 +1,40 @@
1
+ # Sources, translation and scope
2
+
3
+ This skill is an English AnimaGuru adaptation of [Huashu Art Motion](https://github.com/alchaincyf/huashu-art-motion), by alchaincyf (Huashu), discovered through [the Voxyz_ai post](https://x.com/Voxyz_ai/status/2109690569563746755).
4
+
5
+ - Pinned source revision: `2571ee0a9a0467c4d99e935ea9e0fadda19210b0`.
6
+ - Reviewed on October 15, 2026.
7
+ - Upstream code and documentation: MIT, with the original copyright and license retained in [LICENSE](../LICENSE).
8
+ - Coverage: all 36 art recipe cards and all nine explainer grammar cards, mapped individually in [coverage.json](coverage.json).
9
+
10
+ The post described 35 art styles. At the pinned revision the repository has 36: its latest standalone SVG cartoon card has source ID `37_rick_morty`, while ID 07 has no card. The older 35-scene gallery and the SVG example use different render paths. Nine grammars are documented; eight have parameterized upstream examples, while the presenter card has a reference film. This skill bundles English reference guidance, not those engines, examples, characters or media.
11
+
12
+ ## What was translated and adapted
13
+
14
+ The [art index](art-styles.md) links English cards for every source recipe. Each retains material construction, useful palette/parameter examples, motif movement, transition ideas and known limitations. The [grammar index](explainer-grammars.md) covers every source grammar's purpose, composition, choreography, camera, timing and common failures. Individual cards link back to the pinned upstream document.
15
+
16
+ Shared methods from the source's analysis, mechanism, still-first routes, code-rendering guide, rhythm/audio, lessons, author contract, grammar integration, character, long-scroll, narration and camera documents are consolidated in [production](production.md) and [rendering](rendering.md). This is a production translation and adaptation, not a line-for-line archive of every demo log or private review conversation.
17
+
18
+ English copy is fitted by real width, voice and reading time. Chinese glyph-count and typing-speed examples do not become English limits. Names, source IDs and original copyright attribution are preserved where they identify provenance. Source numerical values are marked as starting points from upstream studies; historical quality ratings and QA tables are not presented as current AnimaGuru test results.
19
+
20
+ ## Deliberate AnimaGuru changes
21
+
22
+ Use the installed AnimaGuru generation tools, project assets, enabled models, cue sheet, PixiJS kit, player, audio tools and exporter. The upstream Canvas global APIs and author-specific speech endpoints are not installed or assumed. Generated layers, procedural art and hybrid production are all available decisions; advanced character quality should be judged from the actual result.
23
+
24
+ The reusable guidance does not import upstream promotional instructions, update checks, model-brand rankings, fixed demo coordinates, compulsory three-direction reviews, blanket character bans, hue bans, fixed motion percentages or automatic delegation. Those reflected the original author's environment or preferences. It preserves the evidence behind useful lessons: material-specific motion, deliberate cameras, stable random variation, correct sources and numbers, visible contact and honest review.
25
+
26
+ ## Asset exceptions
27
+
28
+ The upstream README separates some assets from MIT code/documentation:
29
+
30
+ - Arphic-licensed stroke median data has its own terms.
31
+ - Bundled fonts have their own licenses, including OFL 1.1.
32
+ - The author's likeness, character sheets and footage containing that likeness are demonstration material.
33
+ - The Clawd mascot belongs to Anthropic; fan-demo footage is not general MIT media.
34
+ - Some museum artwork is separately designated CC0.
35
+
36
+ No such assets, voices, fonts, stroke data, footage or mascots are redistributed with this skill. The MIT notice does not authorize their reuse. Source styles referencing artists, studios or programs identify influences; use original or authorized subjects. When adding assets later, check their individual terms and preserve their notices.
37
+
38
+ ## Extending the references
39
+
40
+ Record the exact source revision and distinguish an observed result from a hypothesis. Keep project-specific private evidence in that project's production records. A stable general lesson can update the relevant card; correct an obsolete sentence instead of accumulating contradictory rules. If copying substantial new source material, preserve applicable attribution and confirm asset exceptions again. The temporary analysis checkout is not a runtime dependency.
@@ -21,6 +21,8 @@ Read the sibling `animaguru` skill for authentication, model discovery, credit a
21
21
 
22
22
  Keep the gallery URL in `galleryUrl`. All available style skills are already installed for both agents; selection does not require another CLI command, download, account or paid generation.
23
23
 
24
+ An explicit request for the installed `animaguru-art-motion` skill is a custom style choice. Read its `SKILL.md`, select the relevant art material and/or explainer reference, and continue the shared brief without asking for another gallery choice. Save `skill` as `animaguru-art-motion` and leave `template` null; record the selected reference names in `production/brief.md`. The skill contains 36 art treatments and nine motion grammars as references, rather than additional gallery presets.
25
+
24
26
 
25
27
  ## Purpose workflows and saved projects
26
28
 
@@ -77,7 +79,7 @@ Before choreographing a scene from scratch, search the AnimaGuru motion library
77
79
 
78
80
  ## Asset library
79
81
 
80
- Pro and Team accounts can take ready-made music, sound effects, voice-overs, clips, images and sprites from the AnimaGuru asset library instead of generating them: `animaguru library search DESCRIPTION --type music|sfx|speech|video|image|sprite` (natural language and keywords). `animaguru library download ID --output public/assets/` saves a file into the film (sprites save their sheets with `animation.json` frame rectangles and timing); `animaguru library import ID --project PROJECT_ID` adds it to a cloud project instead. Prefer a library whoosh or background track over generating one; it spends no credits.
82
+ Pro and Team accounts can take ready-made music, sound effects, voice-overs, clips, images and sprites from the AnimaGuru asset library instead of generating them: `animaguru library search DESCRIPTION --type music|sfx|speech|video|image|sprite` (natural language and keywords). `animaguru library download ID --output public/assets/` saves a file into the film (sprites save their sheets with `animation.json` frame rectangles and timing); `animaguru library import ID --project PROJECT_ID` adds it to an AnimaGuru project's assets instead. Prefer a library whoosh or background track over generating one; it spends no credits.
81
83
 
82
84
  ## Voiceover, music and captions
83
85
 
@@ -115,7 +117,7 @@ When the user requests hosting or sharing, use `animaguru submit --yes` from thi
115
117
 
116
118
  ## Cloud versions
117
119
 
118
- If the workspace has `.animaguru/sync/link.json`, it syncs with an AnimaGuru cloud project. Save milestones the user would want to return to with `animaguru cloud push -m "MESSAGE"`, such as an approved brief, finished scenes or a delivered export. Run `animaguru cloud pull` when push reports a newer version. Do not link a folder or delete a cloud project unless the user asks. Read the local AnimaGuru skill's cloud section for conflicts, restores and plan rules.
120
+ If the workspace has `.animaguru/sync/link.json`, it syncs with an AnimaGuru project's animations. Save milestones the user would want to return to with `animaguru cloud push -m "MESSAGE"`, such as an approved brief, finished scenes or a delivered export. Run `animaguru cloud pull` when push reports a newer version. Do not link a folder or delete a project's animations unless the user asks. Read the local AnimaGuru skill's cloud section for conflicts, restores and plan rules.
119
121
 
120
122
  ## Multi-animation workspace
121
123
 
@@ -36,4 +36,4 @@ When asked to host or share the finished film, read the local AnimaGuru skill's
36
36
 
37
37
  ## Cloud versions
38
38
 
39
- A linked folder has `.animaguru/sync/link.json`. Save milestones the user would want to return to with `animaguru cloud push -m "MESSAGE"`; run `animaguru cloud pull` when push reports a newer cloud version. Ask before resolving files changed on both sides, restoring over unsaved work, linking a new folder or deleting a cloud project. The AnimaGuru skill's cloud section has the details.
39
+ A linked folder has `.animaguru/sync/link.json`. Save milestones the user would want to return to with `animaguru cloud push -m "MESSAGE"`; run `animaguru cloud pull` when push reports a newer cloud version. Ask before resolving files changed on both sides, restoring over unsaved work, linking a new folder or deleting a project's animations. The AnimaGuru skill's cloud section has the details.
@@ -104,4 +104,4 @@ After signing in with `animaguru auth login`, run `animaguru submit --yes` here
104
104
 
105
105
  ## Cloud versions
106
106
 
107
- On Individual Pro or Team, `animaguru cloud link` creates a cloud project for this folder. `animaguru cloud push -m "What changed"` then saves a version, `animaguru cloud pull` brings in versions saved elsewhere, and `animaguru cloud clone PROJECT_ID` continues on another computer. `animaguru cloud restore VERSION` returns to an earlier version; push afterwards to keep it. Dependencies, exports, frames, `.env` files, credentials and anything in `.gitignore` or `.animaguruignore` stay local. The studio's Cloud page lists every version and its files.
107
+ On Individual Pro or Team, `animaguru cloud link` syncs this folder with a new AnimaGuru project, or with an existing one through `--project PROJECT_ID`. `animaguru cloud push -m "What changed"` then saves a version, `animaguru cloud pull` brings in versions saved elsewhere, and `animaguru cloud clone PROJECT_ID` continues on another computer. `animaguru cloud restore VERSION` returns to an earlier version; push afterwards to keep it. Dependencies, exports, frames, `.env` files, credentials and anything in `.gitignore` or `.animaguruignore` stay local. The project's Animations tab in the studio lists every version and its files.
@@ -13,9 +13,10 @@ const RATE = 48000;
13
13
  export const MIX_DEFAULTS = { musicDb: -9, duck: { threshold: 0.03, ratio: 6, attack: 20, release: 350 }, loudness: { i: -14, tp: -2, lra: 9 } };
14
14
 
15
15
  /** The music part of the graph for input `input`, as edited by audio:music (a trim or a bar-aligned cut). */
16
+ // Padding counts samples at RATE: the bundled Linux ffmpeg (@ffmpeg-installer) has no apad whole_dur option.
16
17
  export function musicGraph(edit, { input = 0, duration, musicDb = MIX_DEFAULTS.musicDb, label = "music" }) {
17
18
  const headIn = Math.max(0, Number(edit?.headIn ?? 0)), fadeIn = Number(edit?.fadeIn ?? (headIn > 0.05 ? 0.25 : 0.02)), fadeOut = Number(edit?.fadeOut ?? 0.6);
18
- const ending = `atrim=0:${duration},apad=whole_dur=${duration},afade=t=out:st=${round(Math.max(0, duration - fadeOut))}:d=${fadeOut},volume=${musicDb}dB,aformat=sample_fmts=fltp:sample_rates=${RATE}:channel_layouts=stereo[${label}]`;
19
+ const ending = `atrim=0:${duration},apad=whole_len=${Math.round(duration * RATE)},afade=t=out:st=${round(Math.max(0, duration - fadeOut))}:d=${fadeOut},volume=${musicDb}dB,aformat=sample_fmts=fltp:sample_rates=${RATE}:channel_layouts=stereo[${label}]`;
19
20
  if (edit?.type === "cut") {
20
21
  const crossfade = Number(edit.crossfade ?? 0.02), cutA = Number(edit.cutA), cutB = Number(edit.cutB);
21
22
  if (!(cutA > headIn) || !(cutB >= 0)) throw new Error("A music cut needs cutA after headIn and a non-negative cutB.");
@@ -37,7 +38,7 @@ export function mixGraph({ music, vo, edit, duration, musicDb = MIX_DEFAULTS.mus
37
38
  if (music) filters.push(...musicGraph(edit, { input: 0, duration, musicDb }));
38
39
  if (vo) {
39
40
  // Mono VO, a little presence and gentle compression, centred in stereo.
40
- filters.push(`[${voInput}:a]aresample=${RATE},aformat=channel_layouts=mono,highpass=f=80,equalizer=f=3200:t=q:w=1.2:g=2,acompressor=threshold=-20dB:ratio=3:attack=5:release=120:makeup=3dB,pan=stereo|c0=c0|c1=c0,aformat=sample_fmts=fltp:channel_layouts=stereo,apad=whole_dur=${duration},atrim=0:${duration}${music ? ",asplit=2[vo][key]" : "[mix]"}`);
41
+ filters.push(`[${voInput}:a]aresample=${RATE},aformat=channel_layouts=mono,highpass=f=80,equalizer=f=3200:t=q:w=1.2:g=2,acompressor=threshold=-20dB:ratio=3:attack=5:release=120:makeup=3dB,pan=stereo|c0=c0|c1=c0,aformat=sample_fmts=fltp:channel_layouts=stereo,apad=whole_len=${Math.round(duration * RATE)},atrim=0:${duration}${music ? ",asplit=2[vo][key]" : "[mix]"}`);
41
42
  }
42
43
  if (music && vo) {
43
44
  filters.push(`[music][key]sidechaincompress=threshold=${duck.threshold}:ratio=${duck.ratio}:attack=${duck.attack}:release=${duck.release}:makeup=1[ducked]`);
@@ -140,7 +140,7 @@ async function main() {
140
140
  labels.push(label);
141
141
  }
142
142
  }
143
- filters.push(`${labels.join("")}amix=inputs=${labels.length}:normalize=0,apad=whole_dur=${duration},atrim=0:${duration}[out]`);
143
+ filters.push(`${labels.join("")}amix=inputs=${labels.length}:normalize=0,apad=whole_len=${Math.round(duration * SAMPLE_RATE)},atrim=0:${duration}[out]`);
144
144
  const work = path.join(base, "production/audio/work");
145
145
  await mkdir(work, { recursive: true });
146
146
  const track = path.join(work, "vo-track.wav");