Skip to content

feat(LTX25-AUDIO-RESAMPLE): resample the take instead of refusing it, and the window is hann - #2585

Open
localai-org-maint-bot wants to merge 4 commits into
mainfrom
row/LTX25-AUDIO-RESAMPLE
Open

feat(LTX25-AUDIO-RESAMPLE): resample the take instead of refusing it, and the window is hann#2585
localai-org-maint-bot wants to merge 4 commits into
mainfrom
row/LTX25-AUDIO-RESAMPLE

Conversation

@localai-org-maint-bot

Copy link
Copy Markdown
Collaborator

feat(LTX25-AUDIO-RESAMPLE): resample the take instead of refusing it, and the window is hann

Closes #2583. Order 7 of .agents/specs/ltx25-completion-scope.md §8, gap A19, which that plan places before A3 and A18 because it gates both.

The gap

Ltx2WaveformToLogMel refused every sample rate but the audio VAE's own (ltx2_audio_vae.cpp:1051-1060), and Ltx2DecodeAudioWav refused it one hop earlier so the message could name the file (ltx2_audio_input.cpp:96-105). Between them, an audio-to-video render only accepted a take the caller had already resampled with some other tool.

Upstream refuses nothing here. waveform_to_mel calls resample_audio before the mel transform (ops.py:44-49) and resample_audio filters whenever the rates differ (ops.py:36-42).

The reason written beside that refusal was wrong, in nine places

The message and eight sibling comments called torchaudio.functional.resample "an arbitrary-ratio polyphase kaiser resampler". It is not one. resample defaults resampling_method="sinc_interp_hann" (torchaudio 2.11 functional/functional.py:1441) and only the kaiser branch (:1386-1391) builds i0-weighted taps; ops.py:40 passes neither that argument nor beta. The window is the same hann this tree already builds for the vocoder's BWE stage — Ltx2HannSincResampleFilter1d is this kernel at orig_freq == 1. What was missing was the rational ratio, not the filter.

All nine are repaired, including the two in ltx25-a2v-audio-input.md, the row that wrote them. polyphase kaiser and kaiser resampler now return zero hits across src, include, tests, docs and .agents.

The port

Ltx2ResampleWaveform mirrors resample at every default: gcd-reduced ratio, lowpass_filter_width = 6, rolloff = 0.99, one kernel row per output phase, convolution with stride orig, and truncation to ceil(new * length / orig). It is called from Ltx2WaveformToLogMel unconditionally, because upstream's rate test lives inside resample_audio (ops.py:38-39); guarding at the call site would have moved that early return off the production path.

Ltx2DecodeAudioWav loses its want_sample_rate parameter. Upstream's decoder has no target rate, and the parameter existed only to raise.

The dtype is f32, and that is the mirror

Upstream's waveform is float32 from the decoder on (decode.py:173-176), encode_audio moves it without casting (audio_vae.py:271), and the kernel is built with dtype=waveform.dtype, so the filter itself is f32 (:1376-1397). Measured against the pinned oracle at four rate pairs:

Filter built in max abs diff vs the oracle
double, narrowed to float 1.03e-05
float, mirroring upstream 1.79e-07

The double filter is more accurate than upstream and therefore fifty-seven times further from it. It would have passed a gate written around it and read as care. This is AGENTS.md's "a token gate cannot detect a dtype that is too wide" in its exact shape.

The gate is 2.5e-07, twice the measured floor: at 0.0 three of the four arms red at 5.96e-08, 5.96e-08 and 1.19e-07, and the equal-rate arm passes because it is a copy. A double-built kernel fails it by forty.

Tests

Upstream ships none at this pin, so sections 8d and 8e of gen-ltx2-vae-goldens.py EXECUTE upstream's own AudioProcessor — at four ratios chosen for arms rather than coverage (o = 1, n = 1, o = 441 with width = 17, and the equal-rate early return), and through waveform_to_mel at a non-target rate. They ride the generator's existing revision anchor, and regeneration is byte-identical.

Red before, green after. Both reds were the refusal itself:

Before After
test_ltx2_vae THREW at ltx2_audio_vae.cpp:1053 48/48 cases, 3217/3217 assertions
test_ltx2_video THREW 'high.wav' is sampled at 44100 Hz ... 110/110 cases, 4870/4870 assertions

Reachability. The video case enters through LoadVideoEngine + Generate and pairs a 44.1 kHz take with the same PCM bytes relabelled 24 kHz — the wrong answer the refusal existed to prevent. Deleting the production call site makes those two carry the same latent digest (18285287296143238670) while the unit case over Ltx2ResampleWaveform stays green, which is the whole point of the mutation. The tree was restored byte-for-byte and both suites re-run green.

One assertion was written, run, and removed rather than loosened. audio_latent_absmax reads 1.07194 / 1.07293 / 1.07208 across three genuinely different takes, a 0.1% spread, because the trace's absmax is dominated by the encoder's per-channel statistics. It cannot order them. The reason is recorded where the assertion was, and the values are gated at the VAE level instead.

Also

This file's own scope note claimed the analysis half was not ported. The mel front-end has lived here since the A2V input row, so that sentence was already false; repaired in flow.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]

…s window is hann (#2583)

`ltx25-completion-scope.md` places A19 at order 7 because it gates A3 and A18:
`Ltx2WaveformToLogMel` refuses every rate but the audio VAE's own, so any real
take has to arrive pre-resampled. Upstream refuses nothing here; `ops.py:44-49`
calls `resample_audio` before the mel transform, unconditionally.

The reason written beside that refusal, in nine places across this tree, is
wrong. It calls `torchaudio.functional.resample` "an arbitrary-ratio polyphase
kaiser resampler". `resample` defaults `resampling_method="sinc_interp_hann"`
(torchaudio 2.11 functional.py:1441) and only the `sinc_interp_kaiser` branch
builds a kaiser window (:1384-1391); `ops.py:40` passes neither that argument nor
`beta`. The window is hann -- the same family `Ltx2HannSincResampleFilter1d`
already ports for the vocoder's BWE stage, which is this kernel at
`orig_freq == 1`. What is missing is the rational ratio, not the filter.

The spec's §4 records the measurement that decides the dtype, because it is the
one place this port could quietly go wrong in a way no gate would see. Upstream
resamples in float32 all the way down, kernel included, and building the same
filter in double lands 1.03e-05 from the oracle where float lands 1.79e-07 -- a
wider dtype reading as a looser gate rather than as an error.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
… and the window is hann (#2583)

Closes #2583. Order 7 of `ltx25-completion-scope.md` §8, gap A19.

`Ltx2WaveformToLogMel` refused every sample rate but the audio VAE's own, and
`Ltx2DecodeAudioWav` refused it one hop earlier so the message could name the
file. Between them, an audio-to-video render only accepted a take the caller had
already resampled with some other tool. Upstream refuses nothing here:
`waveform_to_mel` calls `resample_audio` before the mel transform (ops.py:44-49)
and `resample_audio` filters whenever the rates differ (ops.py:36-42).

`Ltx2ResampleWaveform` is `torchaudio.functional.resample` at the arguments
ops.py:40 passes, which is to say at every default: gcd-reduced rational ratio,
`lowpass_filter_width = 6`, `rolloff = 0.99`, one kernel row per output phase,
conv with stride `orig`, and truncation to `ceil(new * length / orig)`. It is
called from `Ltx2WaveformToLogMel` unconditionally, because upstream's rate test
lives inside `resample_audio` and guarding at the call site would move that early
return off the production path.

The filter is HANN, and nine statements in this tree said kaiser. `resample`
defaults `resampling_method="sinc_interp_hann"` (torchaudio 2.11
functional.py:1441) and only the kaiser branch (:1386-1391) builds i0-weighted
taps; ops.py:40 passes neither that argument nor `beta`. It is the same window
`Ltx2HannSincResampleFilter1d` already builds for the vocoder's BWE stage, which
is this kernel at `orig_freq == 1`. What was missing was the rational ratio. All
nine are repaired here, including the two in `ltx25-a2v-audio-input.md`, which is
the row that wrote them.

THE DTYPE IS f32 AND THAT IS THE MIRROR, NOT A SHORTCUT. Upstream's waveform is
float32 from the decoder on (decode.py:173-176), `encode_audio` moves it without
casting (audio_vae.py:271), and the kernel is built with `dtype=waveform.dtype`,
so the filter itself is f32 (:1376-1397). Measured against the pinned oracle at
four rate pairs, a `double`-built kernel narrowed to `float` lands 1.03e-05 away;
the f32 one lands 1.79e-07. Widening would not have improved the port, it would
have loosened the gate by fifty-seven and read as care.

The gate is 2.5e-07, twice the measured floor: at 0.0 three of the four arms red
at 5.96e-08, 5.96e-08 and 1.19e-07, and the equal-rate arm passes because it is a
copy. Sections 8d and 8e of the golden generator run upstream's own
`AudioProcessor` at those four ratios and through `waveform_to_mel` at a
non-target rate, riding the existing revision anchor.

Both reds were the refusal itself. `test_ltx2_video`'s case enters through
`LoadVideoEngine` + `Generate` and pairs a 44.1 kHz take with the same PCM bytes
relabelled 24 kHz, which is the wrong answer the refusal existed to prevent:
deleting the production call site makes those two carry the SAME latent digest
while the unit case over `Ltx2ResampleWaveform` stays green.

One assertion was written, run, and removed rather than loosened.
`audio_latent_absmax` reads 1.07194 / 1.07293 / 1.07208 across three genuinely
different takes, so it cannot order them; the reason is recorded where the
assertion was and the values are gated at the VAE level instead.

This file's own scope note claimed the analysis half was not ported. The mel
front-end has lived here since the A2V input row, so that sentence was already
false; repaired in flow.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
…TX-2 file

`origin/main` moved 21 commits ahead while this row built and gated. The only
overlap is `docs/FEATURES.md`, where both sides edited different rows; the
resolution keeps both, and `git diff origin/main -- docs/FEATURES.md` shows
exactly one changed line, the A2V row this row owns. No source, header, test or
golden this row touches appears in the incoming set, so nothing it measured moved
under it. Re-verified after the merge: `polyphase kaiser` and `kaiser resampler`
return ZERO hits across `src`, `include`, `tests`, `docs` and `.agents`, which is
the claim the implementation commit makes about all nine sites.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
… could falsify

`origin/main` moved another eight commits while this row finished its gate. The
overlap is `docs/FEATURES.md` alone, on different rows; `git diff origin/main --
docs/FEATURES.md` is the one A2V line this row owns. Nothing in the incoming set
touches an LTX-2 source, header, test or golden.

Re-checked rather than assumed, because merging main has falsified a branch's own
prose here before: `polyphase kaiser` and `kaiser resampler` still return ZERO
hits across `src`, `include`, `tests`, `docs` and `.agents`, which is the claim
the implementation commit makes about all nine sites.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LTX25-AUDIO-RESAMPLE: A19 - the audio front-end refuses every rate but the VAE's own, and the resampler it names is hann, not kaiser

2 participants