Audio-safe AB2/Heun: confine 2nd-order terms to video in both samplers
The FunPack Hybrid Euler 2S and Distilled Flow samplers applied AB2 denoised extrapolation and the Heun corrector to the whole packed LTXAV latent, audio slice included -> degraded audio. (Stock euler/ddim are clean because they're 1st-order; ComfyUI "ddim" == euler.) Confine both to the video stream via _video_only so audio always rides plain 1st-order euler on the raw model denoised, regardless of order/correction settings. Distilled Flow s_noise diversity injection is now video-only too. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
5729166bf8
commit
a44725256e
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
**Audio corruption in the FunPack Hybrid Euler 2S and Distilled Flow samplers on LTXAV.** Both samplers' second-order machinery — the AB2 (Adams-Bashforth) denoised extrapolation and the Heun predictor-corrector — was applied to the *whole* packed latent, including the audio stream, which degraded audio. (Stock `euler`/`ddim` sound fine because they're plain 1st-order with no AB2/Heun; `ddim` in ComfyUI is literally `euler`.) Audio now always rides the plain 1st-order euler update (AB2 and Heun confined to the video stream via `_video_only`), so audio stays clean at any `order`/`correction_blend`/`final_correction_steps` setting while video keeps the 2nd-order treatment. Distilled Flow's optional `s_noise` diversity injection is likewise now video-only. For the deterministic "ddim look" plus FunPack steering, run Distilled Flow at `order=1, final_correction_steps=0` (pure euler base) — audio is doubly safe.
|
||||
|
||||
## [2.7.8] - 2026-06-04
|
||||
|
||||
### Added
|
||||
|
||||
+13
-2
@@ -932,6 +932,8 @@ def _sample_const_rf_full(model, x, sigmas, extra_args, callback, disable,
|
||||
denoised_eff = denoised
|
||||
else:
|
||||
denoised_eff = denoised
|
||||
# Audio rides plain euler — exclude it from AB2 (2nd-order extrapolation corrupts audio).
|
||||
denoised_eff = _video_only(denoised_eff, denoised, video_mask)
|
||||
prev_denoised = denoised.detach()
|
||||
prev_h = h
|
||||
|
||||
@@ -957,6 +959,8 @@ def _sample_const_rf_full(model, x, sigmas, extra_args, callback, disable,
|
||||
d_use = d1 + effective_blend * ((d1 + d2) * 0.5 - d1)
|
||||
else:
|
||||
d_use = d1
|
||||
# Audio rides the plain euler direction (d1); only video gets the Heun correction.
|
||||
d_use = _video_only(d_use, d1, video_mask)
|
||||
x = x + d_use * dt
|
||||
# Heun changed x with a corrected direction; invalidate AB2 history.
|
||||
prev_denoised = None
|
||||
@@ -1520,6 +1524,10 @@ def sample_funpack_distilled_flow(model, x, sigmas, extra_args=None, callback=No
|
||||
else:
|
||||
denoised_eff = denoised
|
||||
|
||||
# Audio rides plain 1st-order euler: keep the AB2-extrapolated estimate for video,
|
||||
# raw denoised for audio (2nd-order extrapolation corrupts the audio stream).
|
||||
denoised_eff = _video_only(denoised_eff, denoised, video_mask)
|
||||
|
||||
# Store current denoised for the next step's multistep correction.
|
||||
# Reset after a Heun step since x was updated with a corrected direction.
|
||||
prev_denoised = denoised.detach()
|
||||
@@ -1539,7 +1547,9 @@ def sample_funpack_distilled_flow(model, x, sigmas, extra_args=None, callback=No
|
||||
# Corrector: evaluate model at the predicted x and sigma_next.
|
||||
denoised_pred = model(x_pred, sigma_next * s_in, **extra_args)
|
||||
d2 = k_diffusion_sampling.to_d(x_pred, sigma_next, denoised_pred)
|
||||
x = x + (d1 + d2) / 2.0 * dt
|
||||
# Audio rides the plain euler direction (d1); only video gets the Heun correction.
|
||||
d_use = _video_only((d1 + d2) / 2.0, d1, video_mask)
|
||||
x = x + d_use * dt
|
||||
# Heun updates x differently; invalidate multistep history.
|
||||
prev_denoised = None
|
||||
prev_h = None
|
||||
@@ -1549,7 +1559,8 @@ def sample_funpack_distilled_flow(model, x, sigmas, extra_args=None, callback=No
|
||||
if s_noise > 0.0:
|
||||
sigma_up = math.sqrt(max(0.0, float(sigma.item()) ** 2 - float(sigma_next.item()) ** 2))
|
||||
if sigma_up > 0.0:
|
||||
x = x + noise_sampler(sigma, sigma_next) * s_noise * sigma_up
|
||||
# Diversity noise on video only — ancestral-style noise corrupts audio.
|
||||
x = _video_only(x + noise_sampler(sigma, sigma_next) * s_noise * sigma_up, x, video_mask)
|
||||
|
||||
return x
|
||||
|
||||
|
||||
Reference in New Issue
Block a user