External VO, WEM, and lipsync
Voice playback and lip movement are parallel resource chains. A scene line selects external subtitle and voice audio with one localization RUID, while each performer selects a lipsync animation-set slot from the scene root. A working subtitle does not prove either of the other chains.
| Record | Value |
|---|---|
| Practical baseline | Cyberpunk 2077 Windows GOG 2.31a, WolvenKit 8.19.0, ArchiveXL 1.27.0, RED4ext 1.30.0, redscript 0.5.31 |
| Lab 5 external subtitle/VO resource chain | Structurally validated |
| Focused vanilla lipsync slots and localized assets | Observed in vanilla |
| Exact Lab 5 playback | Verify with the Lab 5 subtitle, audio, reload, and cleanup cases |
| Arbitrary WEM encoding and custom lipsync generation | Experimental |
Clean-save requirement: an active scene and its checkpoint may retain actor, line, and interruption state across archive changes. Restart the game after replacing localization or audio resources, then begin acceptance from an untouched pre-scene save. Do not diagnose a new WEM from a save captured during the old line.
Use WolvenKit to author the native resource links. Preparing a WEM or custom facial animation also requires a compatible audio or animation tool.
Three independent lookup chains
one unsigned line RUID
|
+---------------------+---------------------+
| |
v v
ArchiveXL subtitles registration ArchiveXL VO-map registration
-> subtitle map -> locVoiceoverMap
-> subtitle-entry resource -> locVoLineEntry.stringId
-> subtitle entry stringId -> femaleResPath / maleResPath
-> displayed text -> archived WEM bytes
scene actor/player actor
-> lipsyncAnimSet.id
-> scene.resouresReferences.lipsyncAnimSets[id]
-> addressable .anims resource
-> facial/lipsync animation data
| Question | Correct owner |
|---|---|
| Which sentence is displayed? | External subtitle entry selected by the line RUID |
| Which audio file is played? | External locVoiceoverMap entry selected by the same RUID |
| Which gender-specific audio path is used? | femaleResPath or maleResPath on that VO entry |
| Which mouth/gesture animation table is available to an actor? | The actor’s scene-local lipsync slot and the root lipsyncAnimSets collection |
| When does the line start and how long can it run? | scnDialogLineEvent and its owning section |
A WEM is not embedded in screenplayStore, and a lipsync .anims row is not
the VO file. The WEM path does not determine the actor’s slot. The actor’s slot
does not repair a missing VO-map entry.
The external subtitle and WEM route
Lab 5 provides a focused, mod-owned chain:
| Role | Exact depot path |
|---|---|
| Subtitle map | mod\cqa\cqa005\localization\en-us\subtitles\cqa005_subtitles_map.json |
| Subtitle entries | mod\cqa\cqa005\localization\en-us\subtitles\cqa005_subtitles.json |
| VO map | mod\cqa\cqa005\localization\en-us\vo\cqa005_vo.json |
| Synthetic voice asset | mod\cqa\cqa005\localization\en-us\vo\contact_i_85c3283507e7ef2f.wem |
Its line, subtitle entry, and locVoLineEntry all use unsigned RUID
9638591835734011695. The VO entry points both gender fields at the same
synthetic WEM. That is an exact lab choice, not a rule that female and male
paths must be equal.
ArchiveXL registers the subtitle map under
localization.subtitles.en-us and the VO map under
localization.vomaps.en-us. The subtitle map reaches the subtitle-entry
resource; the VO map reaches WEMs through its line entries. The subtitle-entry
resource and each WEM are archive dependencies, not separate ArchiveXL root
registrations in this design.
Use the same unsigned RUID at the semantic join, but keep these other identifiers independent:
| Typed or path domain | Purpose |
|---|---|
scnscreenplayItemId | Joins the timed dialog event to the screenplay line |
scnSceneEventId | Identifies that timed event instance |
scnlocLocstringId.ruid | Joins line, subtitle entry, and VO entry |
| WEM depot path | Locates audio bytes selected by the VO entry |
scnLipsyncAnimSetSRRefId.id | Indexes the scene root’s lipsync rows |
Do not derive one domain from another merely because a filename happens to contain a hexadecimal suffix.
Lipsync ownership and slot cardinality
The scene root property is spelled resouresReferences. Its
lipsyncAnimSets array contains scnLipsyncAnimSetSRRef rows. Every ordinary
actor and player-actor definition selects one row through
scnLipsyncAnimSetSRRefId.id.
For a cooked scene with N addressable rows, every selected ID must satisfy:
0 <= actor.lipsyncAnimSet.id < N
This is a cardinality check, not a quality check. It proves that the lookup is in range; it does not prove that the resource matches the actor’s rig, language, gender, line set, or desired facial performance.
Distinct vanilla rows
Observed in vanilla: in
base\quest\minor_quests\mq007\scenes\mq007_01_gun_found.scene, the non-player
actor selects slot 0 and V selects slot 1. The installed English
localization archive contains distinct assets at:
base\localization\en-us\lipsync\base\quest\minor_quests\mq007\scenes\mq007_01_gun_found\skippy.animsbase\localization\en-us\lipsync\base\quest\minor_quests\mq007\scenes\mq007_01_gun_found\v.anims
That resource proves the two-slot shape for this scene. It does not make those assets appropriate for another actor or permit redistribution.
Avoid duplicate-path slots
Two identical source paths may cook to one addressable import. Inspect the
cooked table before assigning an actor slot 1. Prefer distinct compatible
resources when the NPC and V require different facial performances.
Lab 5 deliberately keeps both actors on slot 0 with the generic resource:
base\animations\facial\generic\interactive_scene\generic_facial_lipsync_gestures.anims
That one-row resource and both indices are Structurally validated. Exact facial and gesture quality remains Experimental.
Check the selected clip and its skeletal reference
An addressable .anims resource is only the first lookup. For a custom line,
inspect the screenplay line’s femaleLipsyncAnimationName and
maleLipsyncAnimationName CNames and confirm that the selected clips exist in
the intended animation set. Keep these clip names separate from the WEM path
and localization RUID. Lab 5 leaves both names at None; it does not teach
custom clip generation.
A clip can import successfully, resolve by name, and match the audio duration while still deforming the actor’s head. When removing unwanted donor body motion, preserve the neutral translation, rotation, and scale reference used by the additive animation.
For a custom facial clip, inspect these separately:
| Check | Why it matters |
|---|---|
| Selected clip exists | A valid animset slot does not guarantee a matching line clip |
| Clip and audio durations agree | Inherited donor motion can extend a short clip |
| Facial keys survive import | Duration alone says nothing about mouth movement |
| Skeletal reference survives import | Removing unwanted body motion must not remove the additive reference pose |
| Rig bone and facial-track ordering agree | A matching bone count alone cannot establish compatibility |
Reopen the cooked asset in WolvenKit and inspect an export made from that asset, rather than checking only the source animation. Compare the skeleton with its compatible rig reference. Joint and key counts depend on the rig; matching those counts alone does not validate the reference pose.
Custom facial playback remains Experimental. Test each line in game, including interruption and reload, before using the clip in a finished scene.
Separate asset validity from lookup validity
Diagnose an audio/lipsync failure in this order:
- Line scheduling: confirm the section reaches the
scnDialogLineEvent, itsscreenplayLineIdresolves, and the duration does not clip the line. - Subtitle join: confirm the exact unsigned RUID, subtitle-map
registration, subtitle-entry path, locale, and matching
stringId. - VO join: confirm VO-map registration, exact
locVoLineEntry.stringId, selected gender path, archive presence, path case, and game/audio logs. - WEM bytes: confirm the authored asset is a complete playable WEM made from audio you have the right to distribute. Validate duration and decode independently of the scene.
- Actor readiness: confirm the intended actor was acquired and the event’s performer maps to it.
- Lipsync cardinality: inspect the cooked/round-tripped root collection and every selected slot, not only the raw source rows.
- Presentation compatibility: only after startup and lookup are stable, evaluate mouth movement, emotion, gesture, rig fit, and interruption.
This ordering distinguishes common boundaries:
| Observation | Most likely branch to inspect first |
|---|---|
| Subtitle absent and audio silent | Line event/RUID or both localization registrations |
| Subtitle present, audio silent | VO map, selected WEM path, WEM presence/bytes, audio logs |
| Audio present, mouth motion wrong | Lipsync resource, actor slot, selected clip name, rig compatibility, line-specific animation data |
| Head stretches only during speech | Additive skeletal reference and rig/track ordering in the cooked clip; compare with the idle appearance |
| Crash at scene launch before text/audio | Actor acquisition and cooked lipsync slot addressability |
| Correct first line, later line silent | Later line RUID, VO entry, WEM path/bytes, event timing |
| Works for one performer configuration only | Gender path, actor identity, slot/resource assignment, or rig compatibility |
Manual WolvenKit authoring procedure
- Start with a mod-owned v5 scene whose actor acquisition and one spoken-line event already round-trip. Record actor IDs, performer IDs, selected lipsync slots, line item ID, event ID, and full unsigned line RUID.
- Create a subtitle-entry resource containing the matching
stringId, then a subtitle map whose entry soft-references that resource. Keep both files under a stable mod depot path. - Create a
locVoiceoverMap. Add onelocVoLineEntrywith the same unsignedstringIdand deliberatefemaleResPathandmaleResPathvalues. - Add original or licensed WEM assets at those exact archive paths. If both fields select one file, document that choice; if they differ, test both selection routes. Test the selected encoder and codec with the resulting WEM.
- Register the subtitle map and VO map under the exact ArchiveXL locale branches. Do not register a subtitle-entry file as if it were the map, and do not register each WEM as a VO map.
- In
resouresReferences.lipsyncAnimSets, add only the lipsync resources the scene actually uses. Give each actor an in-range row ID. Do not manufacture two logical slots by duplicating one path without inspecting the cooked result. - Save and reopen every CR2W in WolvenKit
8.19.0. Convert focused resources to JSON and compare the RUID join, map references, gender paths, root array length, actor slot IDs, and event-to-line join. - Pack the normal project. List the archive and prove every referenced WEM is present at the exact path. Extract the cooked scene for a focused comparison of addressable lipsync rows.
- Fully restart the game, load the untouched pre-scene save, and run the acceptance matrix. Serialization, registration, and archive presence are Structurally validated evidence only.
Do not copy vanilla WEM or lipsync resources into your download. Cite their depot paths and teach readers to inspect their own installation. The synthetic Lab 5 WEM is mod-owned and may be used only under that example’s documented license and provenance.
Acceptance and lifecycle matrix
| Case | Required observation |
|---|---|
| Normal line from untouched pre-scene save | One subtitle, one audio playback, correct speaker, no launch crash |
| Every distinct line RUID | Correct text and WEM; no stale prior line |
| Every supported gender path | Correct path selection and asset, or documented shared-path behavior |
| Every actor slot | In-range after cooking; intended resource and acceptable facial result |
| Save before scene launch | Reload starts one coherent scene and resolves current assets |
| Save between lines | If permitted, no duplicate/omitted line or stale lipsync state |
| Interrupt and return | Audio, subtitle, actor tier, and lipsync recover without a false named exit |
| Stream away and return | Actor/resources reacquire without duplicate playback |
| Reload after named exit | No repeated VO or retained Cinematic AI/lipsync ownership |
| Missing-WEM negative control in a disposable candidate | Expected log/absence without misattributing the failure to lipsync |
Active-line save/reload, arbitrary encoder settings, generated custom lipsync, facial quality, interruption, and stream-return behavior remain Experimental until tested for the exact asset set.