fix: correct MiniMax H3 reference audio encoding - #1886
Conversation
|
Same settings and assets as in #1882 repro: output.mp4Voice matches the reference now at least so it's much better than before, but the speech is still incoherent, which I believe should not happen if the model is asked to reuse the audio exactly... |
|
Thanks for testing this. I downloaded the result and compared it with the original reference from #1882. Whisper Large-v3 transcribes the reference as:
It transcribes the new generated result approximately as:
So I agree that the generated transcript is still incoherent. The fact that the voice now matches the reference is useful confirmation that the repaired reference latent is reaching Ref2VA; the remaining transcript behavior is separate from the stereo encoder corruption fixed here. At present, For generated speech, including the literal sentence in the text prompt should improve semantic adherence, for example:
Using the prompt guide's I think H3's transcript adherence can still be investigated separately. This PR is intentionally narrower: it restores the C++ audio encoder to near-numerical parity with the official PyTorch encoder and fixes the corrupted reference conditioning. It does not implement deterministic soundtrack copying or guarantee exact generated speech. |
|
Even if I edit the prompt file to replace my typo If I add I'm thinking maybe there is an issue either with the way the reference audio latents are ordered or with their positionnal encodings, but i cannot find what exactly. |
|
i found something: diff --git a/src/model/diffusion/minimax_h3.hpp b/src/model/diffusion/minimax_h3.hpp
index d0683166..c9175769 100644
--- a/src/model/diffusion/minimax_h3.hpp
+++ b/src/model/diffusion/minimax_h3.hpp
@@ -835,6 +835,18 @@ namespace MiniMaxH3 {
}
};
+ auto append_condition_audio_positions = [&](int64_t length,
+ float cursor,
+ float w_low,
+ float w_high) {
+ for (int64_t t = 0; t < length; ++t) {
+ for (int channel = 0; channel < 2; ++channel) {
+ float w = channel == 0 ? w_low : w_high;
+ append_position(cursor + static_cast<float>(t) * 2.0f, 0.f, w);
+ }
+ }
+ };
+
float cursor = static_cast<float>(text_len);
if (reference_blocks.empty()) {
float video_duration = 0.f;
@@ -888,7 +900,7 @@ namespace MiniMaxH3 {
w_high = axes.second.back();
}
int64_t count = ref_audio->shape()[0] * 2;
- append_audio_positions(ref_audio->shape()[0], cursor, w_low, w_high);
+ append_condition_audio_positions(ref_audio->shape()[0], cursor, w_low, w_high);
layout.sequence_segments.push_back({row,
row + count,
SequenceKind::CONDITION_AUDIO,output-dirtyfix.mp4Of course this is most likely not the correct way to fix this issue, but that's a clue to whatever is going on. Maybe w_high is not correct for audio? |
|
@stduhpf Thanks again for investigating this. I found a separate generated-audio issue that appears to explain the remaining malformed speech, especially with H3 Turbo at 4 steps: #1908. H3 uses different shifted flow schedules for video (12) and audio (3). The current C++ path converts audio velocity with the local schedule derivative, but then Euler applies that value over a finite video-sigma interval. At low step counts this substantially over-advances the audio trajectory. #1908 uses the exact audio/video sigma interval ratio for each Euler step. I tested the fix with H3 Ref2VA + Turbo, Euler/simple, 4 steps, and the same kind of previously malformed generated audio became clear and correct. I also checked the proposed reference-audio position change against the Hugging Face Diffusers and ComfyUI H3 layouts. Both use channel-major audio packing with the same time position for the two channels ( This still does not turn |
|
Thank you for your contribution. |
* fix: preserve "token_refiner" token for MiniMax H3 LoRAs (leejet#1864) * fix: fail with a message when MiniMax-H3 is run in img_gen mode (leejet#1863) * feat: support INT8 ConvRot safetensors (leejet#1857) * fix: replace free_compute_buffer with runner_done in vae (leejet#1872) * sync: update ggml (leejet#1873) * fix(ci): trigger builds for ggml updates * feat: add taeh3 support (leejet#1874) * fix: prevent gallocr hash overflow in tiny graph-cut segments (leejet#1880) * fix: re-clamp streaming VRAM budget to currently free memory (leejet#1878) * fix: mark graph cuts with both a prefix and a suffix (leejet#1883) * fix: make max_order of lms sampler configurable (leejet#1885) * fix: guard against missing sampler/scheduler names (leejet#1887) * chore: format code * fix: use sd_get_preview_interval() (leejet#1907) * feat: configurable image / video compression (leejet#1909) * feat: support standard Qwen3-VL weights for MiniMax-H3 (leejet#1910) * feat: load scaled FP8 weights without upfront conversion (leejet#1913) * fix: match exact weights in LLM config detection (leejet#1923) * feat: add LTX-2.5 support (leejet#1893) Co-authored-by: leejet <leejet714@gmail.com> * fix: correct MiniMax H3 reference audio encoding (leejet#1886) * fix: correct MiniMax H3 audio Euler steps (leejet#1908) * feat: use backend-native FP8 matmul when supported (leejet#1916) * sync: update ggml * feat: additional `--preview-interval` values (leejet#1915) Co-authored-by: leejet <leejet714@gmail.com> * feat: support numbering for preview images (leejet#1895) * fix: use carrier sampling for MiniMax H3 audio (leejet#1924) * feat: generalize temporal tiling across video VAEs (leejet#1926) * feat: prefetch streamed layers during compute (leejet#1905) Co-authored-by: leejet <leejet714@gmail.com> * refactor: unify runner lifecycles and weight residency (leejet#1940) * feat: add verbose logging and log-level selection (leejet#1941) * feat: enable single-GPU auto-fit with tiered parameter placement (leejet#1942) * fix: reuse graph cut plans across CFG passes (leejet#1943) * refactor: split ggml extensions and move implementations to cpp files (leejet#1945) * fix: preserve K-quantized embedding weights (leejet#1936) * fix: correct SDXL embeddings loading (leejet#1939) * refactor: unify model source and weight lifecycle management (leejet#1956) * docs: reflect GGML_MAX_NAME value change in rpc docs (and in ggml_extend assert) (leejet#1950) * refactor: split generation pipeline out of stable-diffusion.cpp (leejet#1957) * fix: enable VAE decode tiling fallback without auto-fit (leejet#1932) * fix: preserve BF16 embedding weights for get_rows (leejet#1959) * fix: handle invalid option numbers (leejet#1961) * feat: expose the loaded model version name through the public API (leejet#1962) * feat: add SenseNova U1.5 support (leejet#1935) * fix: reuse graph plans when scale parameters change (leejet#1963) * feat: add linear and attention scale overrides (leejet#1964) * feat: preserve explicit backend assignments during auto-fit (leejet#1967) * fix: guard GPU memory capacity and propagate encoding failures (leejet#1958) * fix: bound plain-text runs in parse_prompt_attention regex (leejet#1919) * feat: add Wan2.2 S2V (audio+img-to-video) support (leejet#1925) Co-authored-by: leejet <leejet714@gmail.com> * fix: validate vision projector output dim against LLM hidden size (leejet#1918) * fix: resolve MSVC narrowing conversion warnings (leejet#1969) * feat: Add generation parameters into video metadata (leejet#1901) Co-authored-by: leejet <leejet714@gmail.com> * feat: support external Hugging Face tokenizer JSON files (leejet#1973) * refactor: require external Gemma 2 and GPT-OSS tokenizers (leejet#1974) * feat: support Brownian tree noise in all noise injection samplers (leejet#1899) * fix: use tokenizer-specific pre-tokenization rules (leejet#1975) * fix: remove vision_model. from ununsed tensors (leejet#1983) * perf: eliminate temporary allocations in Philox rounds (leejet#1982) * fix: honor flash attention flag in LLM text encoder attention (leejet#1987) * refactor: remove obsolete unused tensor filtering (leejet#1984) * perf: pad small attention heads to 64 for MMA Flash Attention (leejet#1992) * perf: update ggml for faster direct convolutions (leejet#1993) * perf: accelerate VAE direct 3D convolutions (leejet#1996) * fix: propagate CUDA driver dependency to shared library consumers * fix: prevent clip_preprocess center crop from exceeding the resized image (leejet#1995) * perf: reduce CPU overhead in graph execution and sampling (leejet#1997) * perf: parallelize host tensor elementwise and broadcast ops (leejet#1998) * feat: support building with upstream ggml (leejet#1999) * feat: add Qwen Image 2.1 support (leejet#1994) * feat: restore legacy fp8 handling when building with upstream ggml (leejet#2001) * fix: avoid passing ggml logs as format strings (leejet#2002) * feat: add LLaDA-Image support (leejet#1968) Co-authored-by: leejet <leejet714@gmail.com> * feat: add native CUDA SageAttention support (leejet#2005) * fix: avoid narrowing conversion in SigVQ patch embedding and format code * docs: update CONTRIBUTING.md --------- Co-authored-by: stduhpf <stephduh@live.fr> Co-authored-by: leejet <leejet714@gmail.com> Co-authored-by: LostRuins Concedo <39025047+LostRuins@users.noreply.github.com> Co-authored-by: fszontagh <51741446+fszontagh@users.noreply.github.com> Co-authored-by: Wagner Bruna <wbruna@users.noreply.github.com> Co-authored-by: vmobilis <75476228+vmobilis@users.noreply.github.com> Co-authored-by: Piotr Wilkin (ilintar) <ilintar@gmail.com> Co-authored-by: jk212h20 <101200018+jk212h20@users.noreply.github.com> Co-authored-by: assouan <750048+assouan@users.noreply.github.com> Co-authored-by: nan <zjn32202153@gmail.com> Co-authored-by: Hmission <62598659+Hmission@users.noreply.github.com> Co-authored-by: LED-M <105789115+xledx@users.noreply.github.com> Co-authored-by: Maphist0 <28743569+Maphist0@users.noreply.github.com> Co-authored-by: George <35490284+noctrex@users.noreply.github.com> Co-authored-by: Санька Четвёртый <CAHbKA-IV@mail.ru> Co-authored-by: Lin Xuhao <linxuhao84@gmail.com> Co-authored-by: Fabrice Aneche <akhenakh@users.noreply.github.com>
Summary
Fix MiniMax-H3 stereo reference-audio encoding so the C++ audio VAE matches the official PyTorch implementation.
The current encoder reshapes stereo into a batched
conv1dinput. On the tested GGML Metal backend, convolution output storage interleaves the stream dimension with feature channels, while later layers read each stream's feature channels as contiguous. This corrupts the reference latent before Ref2VA conditioning.This change:
zero_k_biasin causal attention.Fixes #1882.
Verification
Tested on Apple M4 Max with the Metal backend and
minimax_h3_audio_vae_fp32.safetensors.Cross-decoding isolated the failure to C++ encoding:
For the same 3.04-second stereo WAV, fixed C++ versus PyTorch normalized latents:
122 x 2 x 32 x 10.99998370.00043430.45343020.4534051The original latent correlation was approximately
-0.066.A clean checkout at
de298c2builds successfully with:A clean-checkout 4-step Ref2VA smoke test also completed successfully, although its generated output was music and is not used as speech-quality evidence. The latent parity and cross-decoding results above came from an instrumented build of the same encoder implementation. Separate longer repaired-input tests produced intelligible, input-dependent generated speech. H3 still generates a new target soundtrack; this patch does not claim waveform copying or exact transcript preservation.
Scope
This is focused on MiniMax-H3 reference-audio encoding. T2AV primarily exercises the audio decoder and therefore does not expose this encoder defect.
AI assistance
AI tools assisted with investigation and patch preparation. I reviewed the changed lines and validated the implementation against the official PyTorch encoder, cross-decoding, a clean build, and end-to-end runs.