port: duration confirmed at 0.2 percent, window refuted, loop start now an explicit field

They timed the wraps instead of converting them -- a probe stamping read_offset
on arrival, three wraps observed, each from its own loop_end to its own
loop_start, both contexts wrapping at the same instant. Cycle 61.81 s against the
61.93 authored here: 0.2 percent, from a wall clock between decoder events versus
an autocorrelation that never touched the wave.

The window is wrong: loop_start is 11.6 percent into the stream, about ten
seconds, so this export has the right duration over the wrong window -- replaying
the intro every cycle and omitting the tail the game plays.

Not re-cut, on their instruction: the exact start is unmeasured and linearity is
refuted by a 4.4 percent rate variation within one stream. But loop_end_s alone
silently asserted a start of zero, so the entry gains loop_start_s, authored as
0.0 and flagged wrong, with -ss applied before -t so the pair is (start,
duration). Proved before it is needed: loop_start_s=10 yields -ss 10 -t 61.93 and
a 61.930 s output. Restored to 0.0, export byte-unchanged.

My smooth-join check gains a use I could not have anticipated: it explains why a
wrong ten-second window went unheard, because a cut near a zero crossing is
smooth wherever it falls.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
This commit is contained in:
Sylpheed port agent
2026-08-30 10:07:07 +00:00
parent ffe0280679
commit 500924be5c
3 changed files with 103 additions and 2 deletions

View File

@@ -98,6 +98,15 @@ pub struct BgmSpec {
/// exporter therefore trims to this length rather than carrying a loop
/// point the runtime could not honour, and the trimmed tail is content the
/// game never reaches.
/// Seconds into the summed bank where the loop window BEGINS.
///
/// 🔴 Split out from `loop_end_s` on 2026-08-30 because carrying only an end
/// silently asserted a start of zero, and that start is now known to be
/// WRONG — the measured window begins about ten seconds in. An assumption
/// that has to be inferred from the absence of a field is not one a reader
/// can weigh.
#[serde(default)]
pub loop_start_s: Option<f64>,
#[serde(default)]
pub loop_end_s: Option<f64>,
#[serde(default)]
@@ -515,7 +524,13 @@ pub fn export_bgm<S: DiscSource + ?Sized>(
// 🔴 TRIM TO THE MEASURED LOOP REGION. Godot loops a whole file, so the
// region has to be the file; carrying a loop point the runtime cannot
// honour would leave the fade-out playing every cycle.
if let Some(start) = spec.loop_start_s.filter(|v| *v > 0.0) {
argv.push("-ss".into());
argv.push(format!("{start}"));
}
if let Some(end) = spec.loop_end_s {
// A LENGTH, applied after any `-ss`, so the pair is (start, duration)
// and moving the start does not silently change how much is kept.
argv.push("-t".into());
argv.push(format!("{end}"));
}