port: the intro had no dialogue because the voice is a separate asset, and I concatenated it wrongly first
A human play-test heard music under the boot intro and no voices. The obvious reading -- the 5.1 fold dropped the centre channel -- is wrong. `ADV.wmv` carries music and effects only; a cutscene's voice is a separate continuous XMA stream in `sound.pak`, bound to the movie by the manifest in `tables.pak`. Nothing was dropped. The exporter had never been asked for it, so every fidelity measurement in AUDIO-VERIFICATION.md would have come back clean. `audio::export_voice` resolves it with `media::resolve_movie_voice_region` and never by filename: `RT01A`'s voice lives inside `VOICE_ADV.slb`, so a name match is correct on exactly the two movies this port would have spot-checked. Decoded, not authored -- so it runs outside the `authored/audio.json` block. THE FIRST VERSION CONCATENATED THE REGION'S CHUNKS AND WAS WRONG. It produced 359 s of dialogue for a 137 s movie. Decoding and timing each chunk shows two of them equal to six decimals and each spanning the whole movie -- HANDOFF Q10's decoded two-stem shape on a second asset kind -- so they are summed at 1/n. The error was visible only because the first version recorded the decoded length against the movie's instead of clamping to it; the clamp `media`'s own doc comment invites, and which `sylpheed-viewer` applies, would have produced a file of exactly the right duration containing the wrong audio. The dropped leading chunk matches no duration in its region and is NOT closed here. It is the same signature as `BGM_103`'s third sub-wave, already open in BLOCKED.md, now corroborated on an independent asset kind. Raised with the Decoder; the manifest names every chunk dropped and its length. Also in this commit, and separable: * `--skip-at=SECONDS` -- `--script` structurally cannot press during a movie, because `_script_settled` waits while `_player != null`. That is why "does (A) skip the intro" had been read out of the source rather than measured. * MISSION section 6 pins a 5.1->stereo matrix and this exporter has shipped a different one since P4 -- the same weighting, 7.65 dB quieter -- and said so nowhere. Re-measured with the right instrument (float decode, whole file, count the samples that would clamp, not a peak reading): the pinned matrix puts ADV at +4.26 dBFS on 4406 samples, while S00A never clips. So the pin overloads one movie and the constant is over-broad for the other. NOT changed -- the level of a mix is what section 6 reserves to a human. The export now carries a warning with the numbers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
This commit is contained in:
@@ -120,6 +120,7 @@ func _ready() -> void:
|
||||
_shots = args.get("shots", "")
|
||||
if args.has("script"):
|
||||
_script = args["script"].split(",", false)
|
||||
_skip_at = float(args.get("skip-at", "0"))
|
||||
# P5. `--play` boots first and hands over on the title; `--menu` starts on a
|
||||
# screen directly, which is what makes an unattended run cheap -- it does not
|
||||
# sit through 137 s of intro to press a d-pad.
|
||||
@@ -242,6 +243,9 @@ var _play := false
|
||||
var _pending: Variant = null
|
||||
var _script: PackedStringArray = PackedStringArray()
|
||||
var _shots := ""
|
||||
## `--skip-at=SECONDS`: when to send a synthetic (A) during a movie, or 0.
|
||||
var _skip_at := 0.0
|
||||
var _skip_sent := false
|
||||
var _script_started := false
|
||||
var _sequence: Array[Dictionary] = []
|
||||
var _player: VideoStreamPlayer = null
|
||||
@@ -262,6 +266,20 @@ func _process(delta: float) -> void:
|
||||
_overlay_process(delta)
|
||||
|
||||
if _player != null:
|
||||
# `--skip-at=SECONDS` presses (A) at a wall-clock moment DURING a movie,
|
||||
# which `--script` structurally cannot do: `_script_settled` waits while
|
||||
# `_player != null`, so a scripted walk only ever starts after the movie
|
||||
# has ended. That gap is why "does (A) skip the intro" had been read out
|
||||
# of the source rather than measured, and a human play-test then found
|
||||
# it not working.
|
||||
#
|
||||
# It goes through `Input.parse_input_event`, like `_press` -- the wiring
|
||||
# between a press and `_unhandled_input` is the thing under test, so a
|
||||
# direct call to `_video_finished` would prove nothing.
|
||||
if _skip_at > 0.0 and _elapsed >= _skip_at and not _skip_sent:
|
||||
_skip_sent = true
|
||||
print(" --skip-at: pressing (A) at %.2f s" % _elapsed)
|
||||
_press("ui_accept")
|
||||
return
|
||||
|
||||
# A menu transition. This is checked BEFORE the boot sequence and outside
|
||||
@@ -367,6 +385,17 @@ func _play_video(name: String, skippable: bool) -> void:
|
||||
await get_tree().process_frame
|
||||
_player.finished.connect(_video_finished)
|
||||
_player.play()
|
||||
# The dialogue is a SECOND stream, started with the picture. `ADV.wmv` and
|
||||
# `S00A.wmv` carry music and effects only; the voice is a separate asset the
|
||||
# exporter resolves off the movie manifest. Started after `play()` and in the
|
||||
# same frame, because the offset between them is zero and adding a wait here
|
||||
# would be authoring a sync constant nobody measured.
|
||||
if audio.play_voice(name):
|
||||
print(" + voice %s" % name)
|
||||
else:
|
||||
# Said out loud: silence is the audio failure that looks like success,
|
||||
# and "this cutscene is unvoiced" is a real answer for most of the disc.
|
||||
print(" no voice track for %s in this export" % name)
|
||||
|
||||
|
||||
var _skippable := false
|
||||
@@ -374,6 +403,9 @@ var _skippable := false
|
||||
|
||||
func _video_finished() -> void:
|
||||
print(" video ended at %.2f s" % _elapsed)
|
||||
# Before anything else: a voice that outlived a skipped intro would play on
|
||||
# over the title screen, which is the sort of bug that sounds like a feature.
|
||||
audio.stop_voice()
|
||||
_player.queue_free()
|
||||
_player = null
|
||||
# A movie the MENU started (P7) returns to an authored screen; a movie the
|
||||
|
||||
Reference in New Issue
Block a user