Qwen Team, Alibaba — Technical Report, arXiv:2503.20215, March 2025

Qwen2.5-Omni: the Thinker and the Talker

One model that sees, hears, reads — and talks back while it is still thinking. The trick is not making it multimodal. The trick is making it multimodal on a clock.

Prerequisites: what a transformer decoder does (tokens in, next-token distribution out) + what a mel-spectrogram is (a picture of sound over time). Rotary embeddings, audio codecs, and flow matching are all rebuilt from zero inside.
10
Chapters
11
Interactive Sims
40ms
One Temporal ID
56.13
OmniBench Avg

Chapter 0: The Omni Problem

Put yourself on a video call with a machine. You hold up a circuit board to the camera and say, half-way through pointing at it, "wait — is this the capacitor that's blown?" You expect an answer. Not a transcript, not a paragraph of markdown three seconds later. A voice, starting roughly when a person's voice would start, that already knows which component your finger was on when you said "this".

Every part of that sentence is a separate engineering problem, and they fight each other.

The machine has to see (the board, the finger, the motion). It has to hear (your words, and also your tone — the rising urgency of "wait"). It has to read (the datasheet you pasted in the chat earlier). It has to align what it saw with what it heard, because "this" only means anything if the pointing gesture and the word land at the same instant. It has to think, which for a language model means autoregressive text generation. And then it has to talk, which means producing a waveform — 16,000 or 24,000 numbers per second of it — starting before the thinking has finished, or the pause will feel dead.

Qwen2.5-Omni is Alibaba's attempt to do all six inside one end-to-end model. This lesson is about how, and about the three specific inventions that make the last two possible: the Thinker-Talker split, TMRoPE, and a set of block-wise streaming modifications applied to literally every component in the stack.

The obvious solution, and why it hurts

The way almost every production voice assistant worked before 2024 is a cascade: three separate models bolted end to end.

1. ASR — speech to text
A recognizer (Whisper, say) turns your waveform into a string. Everything not in the string is now gone forever.
↓ a string of characters
2. LLM — text to text
A language model reads the string and writes a reply. It has never heard your voice. It cannot see the board unless a fourth model captioned it.
↓ another string of characters
3. TTS — text to speech
A synthesizer reads the reply out loud. It has no idea what the conversation was about, so it guesses the prosody from punctuation.

The cascade works. It is debuggable, each stage is independently improvable, and it dominated deployed systems for good reasons. But look at what leaks out at each seam.

At seam 1, everything non-lexical dies. Your urgency, your accent, the fact that you were whispering, the dog barking behind you, the exact millisecond your finger touched the capacitor. The LLM receives "wait is this the capacitor that's blown" and nothing else. There is no representation left in which "the tone of the question" exists.

At seam 2, all the model's internal state dies. The LLM knew perfectly well that its own answer was bad news, delivered gently. That knowledge lived in its activations. It hands the TTS a string. The TTS has to re-derive tone from the text alone, which is why synthesized speech so often has the correct words and the wrong attitude.

And latency compounds. Three models means three queues, three model-load times, three sets of first-token delays, stacked. Worse, the naive cascade is blocking at every seam: ASR usually waits for you to stop talking, the LLM waits for the full transcript, the TTS waits for a sentence boundary.

The framing that makes this paper make sense. "Multimodal" is not the hard part any more — by 2025 there were plenty of models that could take an image and an audio clip and emit text. The hard part is the clock. A model that answers a spoken question in four seconds is a different product from one that answers in four hundred milliseconds, in the same way that a car that arrives tomorrow is a different product from a car that arrives now. Every architectural choice in Qwen2.5-Omni — the Thinker-Talker split, the 2-second encoder blocks, the sliding-window DiT — is a choice made in service of the clock. Read the paper as a latency paper wearing a multimodality costume.

The three challenges, in the authors' own framing

The introduction of the report is unusually explicit about what must be solved. It names three, and they map almost one-to-one onto the three inventions. Hold this table in mind; it is the skeleton of the entire lesson.

#Challenge (paper's words, compressed)Why it is hardTheir answerChapter
1Systematically joint-train text, image, video and audio so they enhance each other — especially synchronizing the temporal aspects of audio and visual signalsA video frame and an audio frame that happen at the same instant arrive as two completely different token types with no shared notion of "when"TMRoPE + time-interleaving3 & 4
2Manage interference among outputs from different modalities, so training text output and training voice output do not disrupt each otherOne next-token head cannot serve two vocabularies with different statistics without one of them degradingThinker-Talker1 & 5
3Architectures that enable real-time understanding of multimodal input and efficient audio output streaming, reducing initial latencyFull attention over a whole clip is not streamable; a diffusion decoder that needs the whole utterance cannot emit its first sample earlyBlock-wise everything + sliding-window DiT6 & 7

Notice the shape of the argument. Each challenge is a coupling problem — two things that want to be joined but resist. Time couples vision and audio. The output head couples text and speech. Quality couples with context length, and context length couples with delay. The paper's method for each is the same move in three disguises: find the axis along which the two things disagree, and give each one its own machinery on that axis while keeping everything else shared.

The four sources of initial packet latency

Section 2.4 of the report enumerates exactly what makes the first sound arrive late. This is the paper's own decomposition, and we will turn it into a manipulable ledger in Chapter 7. Learn the four terms now:

TermWhat it isWhat the paper does about it
1. Input processing delayThe delay caused by processing multimodal inputs — encoding audio and video before the LLM can even startBlock-wise attention in both encoders (audio in 2-second blocks) so encoding can start before the clip ends
2. First-text to first-voice-tokenThe time from receiving the first text input until the first voice token comes outTalker consumes Thinker's representations as they stream, rather than waiting for a completed reply
3. First-speech-segment to audioThe delay converting the first segment of speech codes into an actual waveformSliding-window block attention in the DiT: only 2 blocks of lookback and 1 of lookahead, plus chunked BigVGAN
4. Architectural floorThe inherent latency of the architecture: model size, FLOPs, and so onNothing clever — this is the irreducible term, and it is why a 7B model is chosen over a 72B one
Term 4 is the honest one. Papers rarely list "our model is simply big and that costs milliseconds" among their latency contributors. Listing it changes how you should read the other three: they are not claiming to have abolished latency, they are claiming to have removed every term that is not a forward pass. Once terms 1, 2 and 3 are collapsed to near zero, what is left is the compute itself — and that is a problem for hardware and quantization, not for architecture.

See it: the cascade versus the omni model, on one clock

Before any machinery, watch the two designs run the same turn side by side. The sim below plays a single exchange: two seconds of video-with-audio come in, the model must produce a spoken reply. Toggle between the cascade and the end-to-end streaming design and watch (a) where the wall-clock time goes, and (b) which information chips survive to the output.

One turn, two architectures — where the time and the information go

Press Run. The upper lane is a cascade (ASR → LLM → TTS); the lower lane is Qwen2.5-Omni's streaming path. The coloured chips are information carried by the input — watch which ones fall off the belt at each seam. The clock is illustrative: the paper reports no wall-clock latency numbers, so treat these as relative shapes, not measurements.

press Run

Two things should be visible. First, in the cascade the stages are serial: nothing in stage 3 can begin before stage 2 has produced something, and with blocking seams on, stage 2 cannot begin before stage 1 has produced everything. Second, the chips. Prosody dies at the ASR seam. Visual grounding never enters at all unless you bolt on a captioner. Speaker identity, emotion, and the precise audio-visual timing are simply not representable in the intermediate format, which is a string.

Turn blocking seams off and the cascade improves a lot — that is streaming ASR feeding a streaming LLM feeding a streaming TTS, which is what good production systems actually do. The chips still die. That is the part architecture cannot fix from outside; it requires the intermediate representation to stop being text.

What Qwen2.5-Omni actually is, in one table

Before we go deep, here is the whole system laid out so you always know which piece we are discussing. Every row is stated in the report; the initialization column is worth memorizing, because it tells you what the model inherited versus what it learned.

ComponentRoleInitialized fromKey spec
Audio encoderWaveform → audio representationsWhisper-large-v316 kHz in, 128-channel mel, 25 ms window / 10 ms hop; one output frame ≈ 40 ms of audio; block-wise attention in 2 s blocks
Vision encoderPixels → visual tokensQwen2.5-VL's ViT≈675M parameters, patch size 14, MLP merges adjacent 2×2 tokens into one, flash attention, native-resolution packing
ThinkerThe brain: understands everything, generates textQwen2.5 (the 7B LLM)Transformer decoder; Qwen byte-level BPE tokenizer, 151,643 regular tokens
TalkerThe mouth: hidden states + text → speech codec tokensTrained for this system (design motivated by Mini-Omni)Dual-track autoregressive transformer decoder; shares all of Thinker's history
qwen-tts-tokenizerThe speech codec vocabulary Talker writes inBuilt for this paperEfficiently represents key speech information; decodable to audio streamingly by a causal decoder
DiT (flow matching)Codes → mel-spectrogramFlow Matching (Lipman et al.)Sliding window block attention: receptive field of 4 blocks = 2 lookback + current + 1 lookahead
BigVGANMel-spectrogram → waveformBigVGAN (modified)Fixed receptive field, run chunk-by-chunk for streaming

Seven components, and only two of them are new inventions (Talker and the tokenizer). Everything else is a well-understood part reused. That is a deliberate strategy and a useful one to notice: the contribution is the wiring, not the parts. The paper's own summary of its key features lists exactly three things, and two of them are wiring: the unified model, TMRoPE, and Thinker-Talker.

What "end-to-end" means here, precisely. It does not mean "there are no modules". It means gradients flow, and representations flow, without passing through a discrete lossy bottleneck between understanding and speaking. Talker receives Thinker's high-dimensional hidden representations, not just its output text. The paper is explicit that both Thinker and Talker "are designed to be trained and inferred in an end-to-end manner", and that the whole thing "operates as a cohesive single model".

The headline claims — your falsification checklist

A good way to read a technical report is to write down, before the results section, exactly what would count as the paper being wrong. Here are the four claims and their tests. We will collect the evidence in Chapter 9.

ClaimTestWhat would falsify it
Adding audio and speech output does not cost you visionCompare against Qwen2.5-VL-7B on image and video benchmarksA systematic gap on MMMU / DocVQA / Video-MME
It beats the dedicated audio model it descends fromCompare against Qwen2-Audio on ASR, S2TT, audio reasoningLosing on Librispeech, CoVoST2, MMAU
Speech instruction following ≈ text instruction followingConvert text benchmarks (MMLU, GSM8K) to speech and re-runThe huge drop that every previous audio LLM showed
Streaming speech output is not a quality compromiseseed-tts-eval WER and speaker similarity versus non-streaming TTS systemsLosing to CosyVoice 2 / MaskGCT / F5-TTS on content consistency

The third row is the one to care about. Every audio language model before this could hear; the question was always whether it could think as well when the instruction arrived as sound instead of text. Qwen2-Audio scored 33.2 on speech-MMLU against Qwen2-7B's 69.3 on text-MMLU — less than half. If that gap closes, spoken interaction stops being a degraded mode and becomes a real interface. Hold the number 65.6 in your head; we will meet it in Chapter 9.

A map of where we are going

Chapters 1–2 — the anatomy
Thinker and Talker, and how waves and pixels become tokens with actual shapes and counts
Chapters 3–4 — the clock
TMRoPE derived from scratch with a fully worked position-ID example, then the 2-second interleave
Chapters 5–7 — the mouth and the latency
Why Talker needs two different inputs, block-wise streaming everywhere, and the TTFA ledger you can manipulate
Chapters 8–9 — the evidence
The three-stage curriculum, Talker's separate ICL → DPO → speaker path, then every benchmark and every honest limit

One reading habit for this lesson specifically. Whenever you see a number that describes time — 40 ms, 2 seconds, 25 ms, 10 ms, 4 blocks — stop and ask "what does this number make possible, and what does it cost?" Every one of them is a knob that trades quality against delay, and the paper's design is essentially a particular setting of those knobs. By Chapter 7 you will be able to turn each of them yourself and watch the ledger move.

One question to hold for the whole lesson

Here it is, and it is worth writing down before we start: what has to be true for a machine to answer you out loud, correctly, about something it just saw, before the silence gets awkward?

Everything that follows is an answer to a clause of that question.

ClauseWhat it demandsChapter
"out loud"A speech generator that does not damage the language model1, 5
"correctly"Perception that preserves meaning through a 640–2,352× compression2
"about something it just saw"Vision and audio on one timeline, so "this" resolves to the right instant3, 4
"before the silence gets awkward"Every stage streaming, every boundary bounded6, 7
— and did any of it cost something?Honest measurement against single-modality specialists8, 9

Why now, and not two years earlier

A useful sanity question about any systems paper: what became possible that was not possible before? Four things converged around 2024.

EnablerWhat it unlocked
Strong open 7B LLMsA brain worth attaching senses to, small enough to serve on one accelerator
Mature vision encoders with native-resolution packingVideo as tokens at a survivable cost
Neural audio codecs with causal streaming decodersSpeech as a token sequence a language model can emit
Flow matchingHigh-quality mel synthesis in a handful of integration steps, cheap enough for real time

Remove any one and the design collapses. Without a causal codec, speech cannot stream. Without flow matching, the codec-to-mel step is too slow. Without cheap video tokens, the vision path blows the context. Without a good small LLM, the whole thing is too expensive to run live.

That is the honest shape of most systems contributions: not a single new idea, but the first coherent assembly of ideas that only recently became compatible. The invention here is the compatibility.

The clock, from the listener's side

One more angle on why latency is the axis, because the engineering only makes sense once you feel the constraint the way a listener does.

In human conversation, the gap between one speaker finishing and the next starting is remarkably short and remarkably consistent — on the order of a couple of hundred milliseconds across languages and cultures. It is short enough that the next speaker must have begun planning their reply while still listening. Turn-taking is not a stop-and-think protocol; it is an overlap protocol.

That single fact reframes every design decision in this paper.

If the machine waits until you stop before it starts……then
Encoding the audioThe whole clip's encode cost lands after you stop — a delay proportional to how long you spoke
Reading the prompt (prefill)Same: a delay proportional to input length, which for video is enormous
Deciding the whole reply before speakingA delay proportional to reply length — the worst term of all
Synthesizing the whole waveformAnother delay proportional to reply length

Four terms, three of them proportional to something the user controls. A design with all four is not slow by a constant; it is slow in a way that gets worse exactly when the conversation gets interesting.

Now read the paper's mechanisms against that table. Block-wise encoding moves row 1 into the time you were still speaking. Chunked prefill moves row 2 there too. The Thinker-Talker handoff removes row 3 — speech starts while the reply is still being decided. The sliding-window DiT removes row 4 — the waveform is emitted block by block. Every mechanism deletes one row.

The reframe worth keeping. "Low latency" sounds like a performance target you approach by making things faster. It is not. It is a structural property you get by ensuring that no stage waits for a quantity that grows with the input or the output. Once that structure is in place, what remains is compute — and compute is somebody else's problem. This paper is almost entirely about the structure.
How to use this lesson. Chapters 0–2 can be read straight through. Chapter 3 is the one to slow down for — it contains the only genuinely new mathematics, and the worked position table there is referenced by every chapter after it. Chapters 6 and 7 form a pair: 6 derives the mechanisms, 7 prices them. If you are here for one thing, make it Chapter 3 for the idea and Chapter 7 for the engineering.

Three papers hold this one up

Qwen2.5-Omni is assembled, not invented from nothing, and knowing what it inherited tells you where to look for the parts this lesson does not have to explain.

AncestorWhat it contributesWhat Qwen2.5-Omni changed about it
Qwen2.5 (Yang et al., 2024)The LLM itself — the Thinker's initialization, the tokenizer, the text abilityTrained further on 1.2T multimodal tokens, which costs some text performance (Ch 9)
Qwen2.5-VL (Bai et al., 2025)The ~675M ViT, patch size 14, the 2×2 token merge, native-resolution packing — and M-RoPEM-RoPE becomes TMRoPE: the temporal axis is re-anchored to absolute time
Qwen2-Audio (Chu et al., 2024)The Whisper-large-v3-derived audio encoder, the 40 ms frame, natural-language prompts instead of tag hierarchiesFull attention becomes block-wise attention in 2-second blocks, for streaming

Read the third column. Each inheritance came with exactly one modification, and each modification serves one of the three challenges. That is unusually clean engineering, and it is a good sign: when a system's changes map one-to-one onto its stated problems, the authors understood their problems.

Two more ancestors sit slightly further back and get named rather than inherited. Mini-Omni (Xie & Wu, 2024) motivates Talker's dual-track design. Flow Matching (Lipman et al.) and BigVGAN (Lee et al.) supply the codec-to-waveform path. Neither is modified beyond the streaming mask.

What this model is not

Worth stating early, because "omni" invites over-reading, and because Chapter 9 will hold the paper to exactly these boundaries.

Why the boundaries matter for reading. A paper that overclaims is easy to dismiss; a paper that claims precisely is harder to evaluate, because you have to check what it actually said. Qwen2.5-Omni is mostly in the second category. Whenever this lesson quotes the report directly — and it does so often — the quote is the boundary. Everything outside it is either derived (and shown), or flagged as filled in.

With the boundaries drawn, we can say what the model is aiming at — and the cleanest way is to decompose "omni" into the capabilities it actually names.

Each of the six below is separable: a system can have any subset. That is what makes the decomposition useful rather than decorative.

Six capabilities, and who had them in early 2025

"Omni" is a marketing word. Underneath it are six separable capabilities, and the interesting fact about the 2024–2025 landscape is that no open model had all six. Laying them out makes the contribution legible.

#CapabilityWho had itWhat it costs
1Read text and reason wellEvery LLMThe baseline. The thing you risk losing by adding the rest.
2See images and videoLVLMs — Qwen2.5-VL, LLaVA, InternVLToken count. Video is the expensive modality by 20:1 over audio.
3Hear audio — speech and non-speechLALMs — Qwen2-Audio, SALMONN, Whisper-based stacksAn encoder plus alignment training.
4Relate what was heard to what was seen, in timeAlmost nobody, wellA shared temporal coordinate system. This is TMRoPE.
5Speak — produce natural speech, not just textTTS systems, bolted onA second output vocabulary, and the interference it brings.
6Do 1–5 with low initial latencyClosed systems (GPT-4o), and Moshi for the audio-only caseStreaming discipline at every stage.

Read the "what it costs" column as the lesson's syllabus. Chapter 2 is capability 2 and 3's cost. Chapters 3 and 4 are capability 4's. Chapters 1 and 5 are capability 5's. Chapters 6 and 7 are capability 6's. Chapter 9 measures whether capability 1 survived.

Capability 4 is the one to keep an eye on. It is the only row where the honest answer is "almost nobody", and it is the row TMRoPE exists to fill.

A back-of-envelope that explains why encoders exist at all

Before any architecture, get a feel for the raw data rates. This is the arithmetic that makes every later design decision feel inevitable.

ModalityRaw rateNumbers per secondAfter encodingCompression
Text~3 words/s spoken~4 tokens/s~4 tokens/s1× — text is already a code
Audio16,000 samples/s16,00025 tokens/s640×
Video (448 px, 2 fps)2 × 448 × 448 × 3 bytes1,204,224512 tokens/s2,352×

Two things follow immediately.

First, why encoders are non-negotiable. A transformer's attention cost is quadratic in sequence length. Feeding raw samples to an LLM is not "expensive" — it is arithmetically impossible at any useful clip length. The encoder's job is to compress by two to three orders of magnitude while keeping the semantics.

Second, why video dominates every budget. Even after a 2,352× compression, video still emits twenty times more tokens per second than audio. Every video-related choice in this paper — the 2×2 merge, the dynamic frame rate, patch 14 — is an attempt to bend that number down without losing what the frames mean.

A habit worth building. Whenever you meet a multimodal architecture, compute its tokens-per-second-of-input for each modality before reading anything else. The ratios tell you where the compute goes, which tells you which knobs the authors were forced to design around. In this paper the ratio is roughly 20 : 1 : 0.16 for video : audio : text, and once you know that, half the design explains itself.

How to read a technical report

This document is a technical report, not a conference paper, and the genre matters for what you should expect.

What you getWhat you do not get
Architecture at the level of "what is connected to what"Layer counts, hidden dimensions, head counts for the 7B configuration
The specific numbers that define interfaces (40 ms, 2 s, 4 blocks, patch 14)Ablations isolating each of those numbers
Large, broad benchmark tables against strong baselinesError bars, seeds, or significance tests
Data volumes (800B / 300B / 100B tokens)Data sources, filtering rules, or licensing
An honest conclusion naming unsolved problemsLatency measurements — despite latency being the design goal

The correct posture is neither credulity nor cynicism. It is: take the interface numbers as ground truth (they are what the authors built), take the benchmarks as directionally reliable (the baselines are real and the comparisons are standard), and treat everything unstated as unknown rather than inferring it. Wherever this lesson fills a gap, it says so.

The vocabulary you will need

Nine terms carry the whole lesson. Each is derived properly when it first appears; this table exists so the names stop being strangers.

TermOne-line meaningFirst derived
ThinkerThe LLM: understands every modality, emits text and the hidden states behind itCh 1
TalkerA separate decoder that turns those hidden states plus the sampled text into speech codesCh 1, Ch 5
Mel-spectrogramEnergy in 128 perceptually spaced frequency bands, one column every 10 msCh 2
Temporal IDA position number that means "this 40 ms of the world", not "the n-th token"Ch 3
TMRoPEM-RoPE (a (t, h, w) position triple) with the temporal component anchored to absolute timeCh 3
Time-interleavingAlternating 2-second chunks of visual then audio tokens in the flattened sequenceCh 4
qwen-tts-tokenizerThe discrete speech vocabulary Talker writes in, decodable causally and streaminglyCh 5
Sliding window block attentionThe DiT's mask: 2 blocks back, the current block, 1 block aheadCh 6
TTFATime to first audio — the paper's "initial packet latency"Ch 7
Check your understanding before moving on. Without scrolling back: which of the four latency terms does a smaller model help with? (Term 4, the architectural floor — and only that one. Shrinking the model does nothing about the encoder's block boundary, the handoff, or the DiT's lookahead, because those are waiting terms, not compute terms.) If that answer felt obvious, you already have the paper's central distinction.
The paper lists four contributors to initial packet latency. Which one does its architecture explicitly not try to remove?

Chapter 1: The Thinker and the Talker

Chapter 0 ended with a coupling problem: one model must emit two different kinds of output, text and speech, and training either one damages the other. This chapter is about the paper's answer, which is so simple it is easy to underrate — and about why the obvious alternative fails.

Start with the failure, because the need has to be manufactured before the solution means anything.

Why not just add speech tokens to the vocabulary?

Here is the design any of us would try first. A language model already emits tokens from a vocabulary. Speech can be turned into tokens too — that is what a neural audio codec does. So: concatenate the vocabularies. Give the model 151,643 text tokens plus, say, 4,096 speech codes, and let one softmax head choose among all of them. Train on interleaved text-and-speech data. Done.

Now count what goes wrong.

Statistical mismatch. Text tokens are slow and information-dense: a 7B model emits maybe 30–60 of them per second of speech-equivalent content, and each carries a large amount of meaning. Speech codes are fast and information-sparse: you need dozens per second per codebook, and any individual one carries almost nothing — it is a fragment of a fragment of a phoneme. Ask one softmax to model both distributions and you have asked it to be simultaneously sharp (over meanings) and smooth (over acoustic detail). The gradients pull the shared trunk in different directions.

Sequence-length mismatch. The same reply, in text, is maybe 40 tokens. In speech codes it is hundreds. If they share one stream, the speech codes dominate the sequence and therefore the loss, and the model's language ability — the thing you spent trillions of tokens building — gets diluted by acoustic bookkeeping.

Interference in the residual stream. This is the subtle one and the one the paper names directly: "it is essential to manage potential interference among outputs from different modalities, ensuring that the training processes for outputs such as text and voice tokens do not disrupt each other." The last few layers of a decoder are specialized for the output head — they arrange the residual stream into a shape that the unembedding matrix can read. If the head must serve two very different vocabularies, those last layers must serve two masters, and the compromise costs both.

The intuition the authors give, and it is a good one. Humans do not solve this by having one organ. You have a brain that forms the thought and a mouth that shapes the sound, and they are connected by a rich, continuous, high-bandwidth channel — not by a text file. The report says the design "is inspired by the way humans utilize different organs to produce various signals, which are simultaneously coordinated through the same neural networks". Two organs, one nervous system. That is the whole architecture in one sentence.

The split

Thinker is a Transformer decoder with an audio encoder and a vision encoder attached. It processes text, audio, images and video; it produces high-level representations; it generates text. It is, functionally, a normal multimodal LLM. If you deleted Talker, you would still have a competent model — it would just be mute.

Talker is a dual-track autoregressive Transformer decoder. It takes in Thinker's high-dimensional representations and the text tokens Thinker sampled, and it emits discrete speech codes. It has no encoders of its own and no independent understanding of the world. Its entire job is: given what the brain is currently thinking and currently saying, produce the sound.

And — this is the part that makes it "end-to-end" rather than "a TTS bolted on" — Talker shares all of Thinker's historical context information. It is not handed a sentence in isolation. It sees the conversation. It sees, in representation form, that the user sounded worried and that the video showed a burnt component.

Encoders
Audio encoder (Whisper-large-v3 init) and vision encoder (Qwen2.5-VL ViT, ~675M). They turn waveforms and pixels into sequences of hidden vectors in the LLM's space.
↓ one interleaved token sequence, positions assigned by TMRoPE (Ch 3–4)
THINKER — the brain
Transformer decoder (Qwen2.5 init). Shared attention fuses all modalities. Emits: (a) text tokens, autoregressively; (b) the hidden states that produced them.
both hidden states and sampled text-token embeddings, streaming
TALKER — the mouth
Dual-track autoregressive decoder. Shares Thinker's full history. Emits qwen-tts-tokenizer speech codes, autoregressively, while Thinker is still writing.
↓ discrete speech codes
Codec decoder
Sliding-window DiT (flow matching) turns codes into mel; modified BigVGAN turns mel into waveform. Both run chunk-by-chunk.

What "division of labour" buys, precisely

The abstract makes a claim that is easy to skim past: the block-wise encoder strategy "effectively decouples the handling of long sequences of multimodal data, assigning the perceptual responsibilities to the multimodal encoder and entrusting the modeling of extended sequences to a large language model. Such a division of labor enhances the fusion of different modalities via the shared attention mechanism."

Unpack it. Two jobs are being separated:

Giving the encoder only the local job is what makes streaming possible and what improves fusion: because the encoder does not consume the long-range budget, all cross-modal integration happens inside the LLM's shared attention, where a video token and an audio token and a text token are all just vectors in one sequence that can attend to each other freely.

The general principle, worth stealing. When you have a perception module and a reasoning module, push all long-range dependency into the reasoning module and make perception strictly local. You gain streaming for free (local means bounded lookahead), you gain cross-modal fusion for free (everything meets in one attention), and you lose nothing, because perception genuinely is local. The failure mode of the alternative — encoders with full attention over whole clips — is that they hoard context they cannot use and destroy streamability in the process.

Explore the architecture

Click through the components below. Each shows what goes in, what comes out, and what would break if you removed it. The Interference toggle switches to the single-head design we rejected above, so you can see where the conflict physically lives.

Thinker-Talker — click a stage to trace the data flow

Tap the boxes. Shape badges use B = batch, T = sequence length, d = model width. Exact widths are not published for the 7B configuration, so d is shown symbolically; every count that is published (40 ms per audio frame, 2×2 vision merge, 151,643 text tokens) is exact.

Tap a stage.

Contrast: Moshi's single stream

Thinker-Talker is not the only way to make a model talk in real time, and the most interesting alternative is worth holding beside it. Moshi (Kyutai, 2024) takes the opposite bet: one stream, with text and audio interleaved inside a single autoregressive model, plus a second parallel stream for the user's audio so the model can listen and speak at the same time.

AxisQwen2.5-Omni (Thinker-Talker)Moshi (single-model dual-stream)
Where speech is generatedA separate decoder (Talker) consuming the LLM's hidden statesThe same transformer that does the language modelling
Text–speech interferenceStructurally avoided: two heads, two vocabularies, two parameter setsManaged inside one model via the "Inner Monologue" — a text stream time-aligned to the audio stream
Duplex behaviourTurn-based. The paper does not claim listening-while-speakingFull duplex by construction: models its own audio and the user's simultaneously
TrainingTalker gets its own three-stage regime (Ch 8), independent of Thinker'sJointly trained, single objective over the multi-stream token grid
Cost of the choiceTwo things to train and serve; speech quality bounded by what the handoff carriesEvery architectural change touches everything; language quality is directly exposed to acoustic loss terms

Which is better is genuinely unsettled, and the honest answer is "different products". Moshi optimizes for conversational dynamics — interruption, overlap, backchannels. Qwen2.5-Omni optimizes for breadth — it also sees, and it keeps a full-strength LLM intact behind the voice. Note which capability each one sacrificed to get the other.

A test you can apply to any "omni" model. Ask: what happens to the language model's own benchmark scores? If speech generation is fused into the trunk, the trunk's text ability is on the line. Qwen2.5-Omni reports its text scores openly (Chapter 9) — they sit between Qwen2-7B and Qwen2.5-7B, which is a real but modest cost. A model that will not show you this number is hiding something.

Code: the shape of the handoff

Here is what the Thinker-Talker interface looks like in pseudocode, at three levels of resolution. First the naive single-head design we rejected:

python — the single-head design (rejected)
# One vocabulary, one head. Simple, and it fights itself.
V_text  = 151643          # Qwen byte-level BPE, regular tokens
V_codec = 4096            # illustrative speech-code vocabulary
head = Linear(d_model, V_text + V_codec)

for step in range(T):
    h = trunk(seq)                       # the SAME residual stream
    logits = head(h[-1])                 # must be sharp over meanings AND
    tok = sample(logits)                 # smooth over acoustics. Pick one.
    seq.append(tok)
# Failure: hundreds of codec steps per ~40 text tokens, so the loss
# is dominated by acoustics and language ability erodes.

Now the Thinker-Talker version. Notice that the loop is the same loop — the handoff happens inside it, per step, which is what makes the speech streaming rather than post-hoc:

python — Thinker-Talker, streaming handoff
def omni_turn(inputs):
    ctx = thinker.prefill(inputs)          # chunked prefill (Ch 6)
    talker.share_history(ctx)             # Talker sees ALL of Thinker's context

    while not done:
        h, logits = thinker.step()         # h: (B, 1, d) hidden state
        text_tok  = sample(logits)         # (B, 1) discrete
        emit_text(text_tok)

        # THE HANDOFF: continuous state + discrete token, both
        cond = talker.fuse(h, embed(text_tok))    # (B, 1, d_talker)
        for code in talker.step(cond):        # several codes per text token
            code_buf.append(code)
            if len(code_buf) >= block + lookahead:   # Ch 6
                mel  = dit.decode_block(code_buf)   # flow matching
                wav  = bigvgan.chunk(mel)
                play(wav)                          # audio is already out

Read the innermost if again. That single condition is the entire streaming story: audio leaves the machine as soon as one block of codes plus one block of lookahead exists — not when the sentence ends, not when the reply ends. Chapter 6 derives why the lookahead is needed and Chapter 7 turns it into a latency ledger.

Why Talker gets the hidden states and not just the text — the one-line version. "As a streaming algorithm, voice generation must anticipate the content's tone and attitude before the entire text is fully generated." You cannot decide how to say a sentence from its first three words. But the model's hidden state already knows where the sentence is going, because it planned it. The hidden state is a leak from the future. Chapter 5 makes this precise, including the equally important reason Talker also needs the discrete tokens.

What each half can and cannot do alone

AblationWhat survivesWhat dies
Remove TalkerA full multimodal understanding model — text output on all X→Text benchmarksAny voice at all. This is precisely Qwen2.5-VL plus audio.
Remove Thinker's hidden-state handoff, keep the textIntelligible speech — you have rebuilt a TTSProsody planned before the sentence ends; context-appropriate emotion; the whole point
Remove the sampled text tokens, keep hidden statesProsody and attitudeReliable pronunciation — see Chapter 5, this is the homophone problem
Remove shared history from TalkerSentence-level TTS qualityAny voice behaviour that depends on the conversation or the video

That table is the argument for the design, stated as four things that break. Keep it: in Chapter 5 we will justify rows 2 and 3 from first principles, and in Chapter 9 we will see the benchmark that row 1 makes possible — the comparison against Qwen2.5-VL that shows the price of adding a mouth.

Three more things before the quiz: the object graph you would actually build, the exact tensor that interference happens in, and a fair hearing for the strongest objection to this design. Take them in that order, because each makes the next one easier to argue about.

The system, as code you could serve

One more pass over the architecture, this time as the object graph you would actually instantiate. Every shape is derived from a rule established in the chapters that follow; nothing here is invented.

python — the whole system, sketched
class Qwen25Omni:
    def __init__(self):
        # --- perception (Ch 2) ---
        self.audio_enc  = WhisperEncoder(mels=128, block_sec=2.0)  # 1 frame / 40 ms
        self.vision_enc = QwenViT(patch=14, merge=2, params=675e6)   # 2x2 -> 1
        self.a_adapter  = Linear(d_audio,  d_model)
        self.v_adapter  = Linear(d_vision, d_model)

        # --- the brain (Ch 1) ---
        self.thinker = Qwen25Decoder(vocab=151643, rope="tmrope")

        # --- the mouth (Ch 5) ---
        self.talker  = DualTrackDecoder(codec="qwen-tts-tokenizer")

        # --- codes to sound (Ch 6) ---
        self.dit     = FlowMatchingDiT(lookback=2, lookahead=1)     # 4 blocks
        self.vocoder = BigVGAN(chunked=True)

    def build_prompt(self, text, audio, frames, fps):
        # Ch 2: encode each modality into the LLM's space
        a = self.a_adapter(self.audio_enc(audio))     # (25*secs,  d_model)
        v = self.v_adapter(self.vision_enc(frames))   # (F*(px/28)^2, d_model)
        t = self.thinker.embed(text)                  # (n_text,   d_model)
        # Ch 3 + Ch 4: interleave in 2 s chunks, assign (t,h,w) ids by wall clock
        seq, pos = interleave_and_position(t, v, a, fps, chunk=2.0)
        return seq, pos                             # (T, d_model), (T, 3)

Notice what the constructor makes obvious: seven components, two of which are new. Talker and the codec are this paper's inventions. Everything else is a checkpoint plus a modification. The value is concentrated entirely in build_prompt and in the handoff — that is, in the wiring.

And notice what build_prompt returns: not one tensor but two. The sequence and its positions are separate objects, computed by separate rules, and Chapters 3 and 4 are about why that separation is the point.

Where the interference physically lives

"Interference" is a vague word until you can point at the tensor it happens in. Let us do that, because it turns an intuition into an argument.

A decoder's final layers are not general-purpose. Over training they specialize into a readout stage: they arrange the residual stream so that multiplying by the unembedding matrix produces well-calibrated logits. Concretely, for a token to be predicted with high probability, the residual vector must have a large component along that token's unembedding row.

Now put two vocabularies in there.

A single residual stream, feeding a single unembedding, must simultaneously be positioned correctly in both geometries at every step. The optimizer resolves this the way optimizers always do: by compromise. And because the speech codes vastly outnumber the text tokens in any training batch, the compromise is not symmetric — it settles closer to the acoustic geometry.

tokens in a reply, text:   ~40
codes for the same reply at 25 codes/s over 8 s:   ~200 per codebook
ratio:   5:1 or worse, before counting multiple codebooks

That ratio is the whole problem in one number. The gradient signal reaching the shared trunk is dominated by the acoustic task, so language modelling — the capability that took trillions of tokens to build — is the thing that degrades.

Separating the heads removes the compromise by construction. Thinker's last layers only ever serve text. Talker's only ever serve codes. Neither has to be two things at once.

Inline check. Suppose you kept one trunk but used two heads on it — a text head and a code head, both reading the same residual stream. Would that fix the problem? Partly: the readout geometries no longer collide, since each head has its own unembedding. But the trunk still receives both gradients, so the shared representation is still pulled two ways, and the sequence-rate imbalance still weights the pull toward acoustics. Thinker-Talker goes further — separate parameters, separate sequences, joined only by a representation that flows one way.

Before the objection, one framing to carry forward: Thinker-Talker is a claim that generation can be modular even when understanding must be unified. All the modalities meet in one attention on the way in; only on the way out do they separate. That asymmetry is deliberate.

Two organs, one nervous system — how far the analogy goes

The authors reach for biology, and analogies deserve to be tested rather than admired.

Claim in the analogyHolds?
Distinct organs produce distinct signalsYes — Thinker emits text, Talker emits speech codes, separate parameters
Coordinated by the same network, not by a messageYes — hidden states cross, not strings
The mouth does not decide what to sayYes — and this is the objection above, honestly inherited from the analogy
Speech feeds back into thoughtNo — in humans it does; here the channel is one-way
The organs run concurrentlyPartly — Talker runs while Thinker generates, but neither hears the user meanwhile

Three rows hold, one fails, one is partial. That is a good analogy: it earns its keep on the parts that motivated the design and it fails exactly where the system's real limitations are.

An objection worth taking seriously

Here is the strongest argument against Thinker-Talker, and it deserves a fair hearing because it is the argument Moshi's authors implicitly made.

The objection: separating thinking from talking recreates, in miniature, the very seam the cascade suffered from. Talker sees Thinker's states but Thinker does not see Talker's. So the speech generator can never influence what is said — it can only render it. A human, choosing words, is partly choosing them because of how they will sound; a Thinker that cannot hear its own mouth cannot do that.

The reply: true, and the paper does not claim otherwise. What it claims is narrower and defensible — that the seam is continuous rather than lexical. The cascade's seam destroyed information (a string is all that crosses). This seam preserves it (hidden states cross). The influence is one-way, but the bandwidth is high.

What it costs: anything requiring speech to feed back into content. Choosing a shorter word because the sentence is running long. Rephrasing because a tongue-twister emerged. Adjusting content mid-utterance because the user started talking — which is the full-duplex problem again, from a different direction.

How to decide which side you are on. Ask whether your application needs speech to change what is said or only how. A dictation assistant, a customer-service agent, a tutor: only how. A negotiation partner, an improv companion, anything conversationally competitive: possibly what. The architecture follows from the answer, and there is currently no design that is best at both.

Five architectures for a talking model, side by side

Do not read these sequentially; read the columns. The interesting comparison is which property each design gets for free and which it must engineer.

DesignIntermediate formatText qualityProsody from contextStreamingDuplex
Cascade (ASR→LLM→TTS)StringsUntouched — a full LLMNo — TTS guesses from punctuationPossible per stage; seams add upBolted on with VAD
Encoder+adapter+LLM (Qwen2-Audio)Continuous, input side onlySlight costN/A — it cannot speakInput yes, output N/ANo
Single-stream token LM (AnyGPT-style)One vocabularyDegrades — interferenceYes in principleYesNo
Thinker-Talker (this paper)Hidden states + tokensSmall, measured cost (Ch 9)Yes — the design goalYes, at every stageNo
Dual-stream duplex (Moshi)Multi-stream token gridExposed to acoustic lossYesYesYes — the design goal

Nobody is winning on every column, and that is the honest state of the field. Each row picked a property to optimize and paid for it somewhere else. The skill is reading which column your product actually needs.

What Mini-Omni contributed

The report names its influence precisely: Talker is "a dual-track autoregressive Transformer Decoder architecture, motivated by Mini-Omni (Xie & Wu, 2024)". Worth understanding what that means, because it is the one piece of Talker's design with a clear published lineage.

Mini-Omni's observation: if you ask a model to generate hundreds of acoustic tokens in a row with only acoustic history to condition on, it drifts. It repeats, it slurs, it wanders into babble — failure modes anyone who has trained an audio LM recognizes. The fix is to carry a text token stream in parallel with the audio stream, so every step is anchored to a symbol.

Why that works: text is a low-entropy, highly structured signal that the model is already extremely good at predicting. Predicting it alongside the acoustics regularizes the whole trajectory — the text track cannot drift without the language model noticing, and the audio track is tied to the text track.

The same idea, discovered twice. Moshi's "Inner Monologue" is the identical insight: text tokens time-aligned with the speech stream, present specifically because they improve the reasoning and coherence of generated speech. Two independent groups, in the same year, concluded that a speech generator needs a text scaffold. When an idea is found twice from different directions, it is usually load-bearing rather than incidental.

What the boundary actually carries

One more level of concreteness. Per generated text token, the handoff transmits:

CarriedTypeWhat it encodesWhat is lost if you drop it
Hidden state hContinuous, (1, d)The sentence plan, register, certainty, the multimodal context that produced itContext-appropriate prosody — you are left with a TTS
Sampled token embeddingContinuous embedding of a discrete choice, (1, d)The exact lexical item, hence the exact phoneme sequenceCorrect pronunciation — "cat" may be spoken as "dog"
Shared KV historyThe whole contextThe conversation, the video, the previous turnsAny voice behaviour that depends on more than this sentence

Notice that the first two are the same shape and the same type, and that they are added rather than concatenated in the sketch above. That is a design choice this lesson is inferring rather than quoting — the report says Talker "receives both high-level representations and embeddings of the text tokens sampled by Thinker" without specifying the fusion operator. Addition, concatenation-then-projection, and cross-attention are all plausible; the argument for why both signals are needed does not depend on which.

Why does the paper argue that a single autoregressive head over a merged text+speech vocabulary is a bad idea?
What does Talker receive from Thinker?

Chapter 2: Perceivation — Waves and Pixels Become Tokens

Chapter 1 drew boxes. This chapter fills them with numbers, because the next two chapters are about aligning audio tokens with video tokens on a timeline, and you cannot align things whose rates you do not know.

By the end of this chapter you will be able to answer, for any clip, exactly how many tokens the model sees and how much real time each one covers. That is the arithmetic TMRoPE operates on.

The audio path, step by step

The report gives the audio front end in one dense sentence: "we resample it to a frequency of 16kHz and transform the raw waveform into a 128-channel mel-spectrogram with a window size of 25ms and a hop size of 10ms. We adopt the audio encoder from Qwen2-Audio, to make each frame of audio representation roughly corresponds to a 40ms segment of the original audio signal."

Five numbers: 16 kHz, 128 channels, 25 ms window, 10 ms hop, 40 ms per output frame. Let us derive the last one from the others, because the gap between 10 ms and 40 ms is where all the downsampling hides.

Step 1 — resample. Whatever came in (44.1 kHz from a phone, 8 kHz from a phone line) is converted to 16,000 samples per second. Why 16 kHz? Because by the Nyquist theorem it represents frequencies up to 8 kHz, and essentially all speech information lives below that. Doubling the rate would double the compute for content that is mostly breath noise.

Step 2 — frame it. A 25 ms window at 16 kHz is

25 ms × 16,000 samples/s = 0.025 × 16,000 = 400 samples per window.

And a 10 ms hop is

10 ms × 16,000 samples/s = 0.010 × 16,000 = 160 samples per hop.

Windows therefore overlap by 400 − 160 = 240 samples, or 60%. Overlap matters: a phoneme boundary that fell exactly on a window edge would be smeared, and overlapping windows guarantee every instant is well inside at least one window.

Step 3 — count mel frames. The hop determines the frame rate. One frame every 10 ms is

1 / 0.010 s = 100 mel frames per second.

Each frame is a vector of 128 numbers — energy in 128 mel-spaced frequency bands. So one second of audio is a 100 × 128 array. This is the "picture of sound" the encoder actually sees.

Step 4 — the encoder downsamples. The paper says the encoder is Qwen2-Audio's, which is itself Whisper's, and that the result is roughly 40 ms per frame. Whisper's stem is two 1-D convolutions, the second with stride 2, taking 100 frames/s down to 50 frames/s (20 ms each). Qwen2-Audio adds a pooling layer with stride 2 on top, which halves it again:

100 frames/s  →conv stride 2  50 frames/s  →pool stride 2  25 frames/s  =  1 / 25 = 0.040 s = 40 ms per frame.

There it is. Every audio token the LLM sees covers 40 ms of real time. Remember that number — the whole of TMRoPE is built on it.

Why 40 ms is a good choice, not an arbitrary one. It is long enough that a single token can contain a whole phoneme-sized event (English phonemes average roughly 70–100 ms, so a phoneme spans 2–3 tokens), and short enough that timing is preserved to within about one video frame at 25 fps. It also makes the arithmetic of alignment exact rather than approximate: 25 audio tokens per second divides cleanly into 1-second and 2-second boundaries, which is precisely what the chunking scheme in Chapter 4 needs.

Worked example: how many tokens is a 4-second clip?

Take a concrete 4-second recording. Every step, no skipping.

StepComputationResult
Raw samples4 s × 16,000 samples/s64,000 samples
Mel frames (no padding)floor((64,000 − 400) / 160) + 1 = floor(63,600 / 160) + 1 = floor(397.5) + 1 = 397 + 1398 frames
Mel frames (centre-padded, the usual convention)64,000 / 160 = 400400 frames — i.e. 100 per second, as promised
Mel array shape(400 frames, 128 mel channels)51,200 numbers
After conv stride 2400 / 2200
After pool stride 2200 / 2100 audio tokens
Sanity check4 s / 0.040 s per token100 ✓

Note the two ways of counting mel frames — 398 versus 400 — and that they differ by exactly the boundary convention. Real implementations centre-pad, which is why "100 frames per second" is exactly true rather than nearly true. This is the kind of off-by-two that ruins alignment code, so it is worth having seen once.

Compression ratio, end to end: 64,000 numbers in, 100 tokens out. Each token carries the information of 640 raw samples. The encoder's job is to make sure those 640 samples' worth of meaning survives.

python — the audio front end, from scratch
import numpy as np

SR, N_FFT, HOP, N_MELS = 16000, 400, 160, 128   # 25 ms / 10 ms / 128 bands

def frames(x, win=N_FFT, hop=HOP):
    # centre-pad so frame k is centred at sample k*hop
    x = np.pad(x, win // 2, mode="reflect")
    n = 1 + (len(x) - win) // hop
    return np.stack([x[k*hop : k*hop+win] for k in range(n)])   # (n, 400)

def log_mel(x):
    F   = frames(x) * np.hanning(N_FFT)          # (n, 400)
    S   = np.abs(np.fft.rfft(F, axis=-1)) ** 2     # (n, 201) power
    M   = S @ mel_filterbank(N_MELS, N_FFT, SR).T   # (n, 128)
    return np.log10(np.maximum(M, 1e-10))

x   = np.zeros(4 * SR)          # 4 seconds -> 64000 samples
mel = log_mel(x)               # (400, 128)   == 100 frames/s
# encoder stem: conv stride 2 -> (200, d); pool stride 2 -> (100, d)
print(mel.shape[0] // 4)      # 100 audio tokens == 4 s / 40 ms

And the library one-liner you would actually run, which produces the identical array:

python — the one-liner
from transformers import AutoProcessor
proc = AutoProcessor.from_pretrained("Qwen/Qwen2.5-Omni-7B")
feats = proc(audio=x, sampling_rate=16000, return_tensors="pt")
# feats["input_features"] -> (1, 128, 400)  == (batch, mels, frames)
# after the encoder: (1, 100, d)            == one token per 40 ms

The vision path, step by step

The vision encoder is Qwen2.5-VL's ViT, about 675 million parameters, trained on a mix of image and video data so it is competent at both. Three published specifics matter for us:

Worked example. A 448 × 448 video frame:

448 / 14 = 32 patches per side → 32 × 32 = 1,024 patch tokens
2×2 merge: 32/2 = 16 per side → 16 × 16 = 256 visual tokens per frame

A smaller 224 × 224 frame: 224/14 = 16 per side, 256 patches, merged to 8 × 8 = 64 tokens. A 112 × 112 frame: 112/14 = 8 per side, 64 patches, merged to 4 × 4 = 16 tokens. We will use the 16-token frame in Chapter 3's position arithmetic because it is small enough to write out entirely.

The 2×2 merge is a latency decision disguised as a quality decision. Video is where token counts explode. At 256 tokens per frame and even a modest 2 frames per second, a 60-second clip is 30,720 visual tokens — and attention is quadratic. The merge divides that by four before the LLM ever sees it. The paper pairs it with a dynamic frame rate: "we sample the video using a dynamic frame rate" specifically "to preserve video information as completely as possible while adapting to the audio sampling rate". Frame rate is not a constant to be looked up; it is negotiated against the audio clock, and Chapter 3 shows what that negotiation costs.

The one strange rule: an image is two frames

"For consistency, each image is treated as two identical frames." Read that twice, because it looks like a typo and is not.

Why would a still image be duplicated? Because the vision encoder was trained on video, and video processing in this family of models operates on frame pairs — temporal patches spanning two consecutive frames. If you feed a single frame to a stack expecting pairs, you either need a special code path or you pad. Duplicating the image gives you a legal two-frame clip in which nothing moves. The temporal component of the position encoding stays constant across the pair (Chapter 3), so the model correctly reads it as "a still".

This is a small detail with a large lesson: uniformity beats special-casing. Every place where images and video would need different handling is a place where a bug can hide and a training-inference mismatch can grow. Paying a 2× token cost on still images to have exactly one code path is a trade the authors clearly considered worth it.

Explore the two front ends

From signal to tokens — both paths, live arithmetic

Top: the audio path, with windows drawn to scale over a waveform — watch the 60% overlap and the two downsampling stages. Bottom: the vision path, with the patch grid and the 2×2 merge. Every number in the readout is computed from the sliders, not hard-coded.

Play with the frame-rate slider and watch the visual token count relative to the audio token count. At 448 px and 2 fps you are spending 512 visual tokens per second against 25 audio tokens — a 20:1 ratio. Video is, by a wide margin, the expensive modality, and every design decision about video in this paper is downstream of that ratio.

Text, for completeness

Text uses Qwen's tokenizer: byte-level byte-pair encoding with a vocabulary of 151,643 regular tokens. Byte-level BPE means there is no out-of-vocabulary failure mode — any byte sequence, in any language or encoding, is representable, at worst one token per byte. The word "regular" is doing quiet work: on top of the 151,643 sit the special control tokens you saw in the ChatML example (<|im_start|>, <|vision_start|>, and so on), which mark modality boundaries in the sequence.

The rates, collected. Memorize this row. Text: variable, ~1 token per 3–4 characters. Audio: 25 tokens per second (40 ms each). Video: (pixels/14/2)2 tokens per frame, at a dynamically chosen frame rate. Image: as video, but two identical frames. Everything in Chapters 3 and 4 is bookkeeping over these three rates.

The counts are settled. What remains is to make one frame's values concrete, and then to justify the two choices — the mel warp and the number 128 — that the counts took for granted.

One mel frame, computed entirely by hand

Everything above was arithmetic about counts. Do one frame's actual values once, on a toy, so nothing about the front end remains a black box.

Shrink the problem: window of 8 samples instead of 400, sample rate 8 Hz instead of 16 kHz, and 3 mel bands instead of 128. The structure is identical; only the sizes shrink.

Step 1 — the windowed samples. Take a pure tone at 2 Hz, sampled 8 times per second, for 8 samples. That is x[n] = sin(2π · 2 · n/8) = sin(πn/2):

x = [ 0,  1,  0,  −1,  0,  1,  0,  −1 ]

Check n = 1: sin(π/2) = 1. ✓   n = 2: sin(π) = 0. ✓   n = 3: sin(3π/2) = −1. ✓

Step 2 — the DFT magnitudes. For a real 8-point signal the FFT gives 8/2 + 1 = 5 useful bins, at frequencies 0, 1, 2, 3 and 4 Hz. A pure 2 Hz sinusoid puts all its energy in bin 2. Computing bin 2 explicitly:

X[2] = ∑n=0..7 x[n] · e−i2π(2)n/8 = ∑ x[n] · ( cos(πn/2) − i sin(πn/2) )

Term by term, using x = [0, 1, 0, −1, 0, 1, 0, −1]:

nx[n]cos(πn/2)−sin(πn/2)contribution
00100
110−1−i
20−100
3−101−i
40100
510−1−i
60−100
7−101−i
X[2] = −4i  →  |X[2]| = 4  →  power |X[2]|2 = 16

Every other bin sums to zero by the same cancellation. So the power spectrum is:

P = [ 0,  0,  16,  0,  0 ]   over bins at 0, 1, 2, 3, 4 Hz

Step 3 — the mel filterbank. Three triangular filters, overlapping, covering the five bins. A plausible small bank:

Bandbin 0bin 1bin 2bin 3bin 4
mel 0 (low)1.00.50.00.00.0
mel 1 (mid)0.00.51.00.50.0
mel 2 (high)0.00.00.00.51.0

Step 4 — multiply. Each mel value is the dot product of a filter row with P:

mel0 = 1.0·0 + 0.5·0 + 0.0·16 + 0 + 0 = 0
mel1 = 0.0·0 + 0.5·0 + 1.0·16 + 0.5·0 + 0 = 16
mel2 = 0 + 0 + 0 + 0.5·0 + 1.0·0 = 0

Step 5 — log compression. Clamp at a floor and take the log, exactly as the real pipeline does:

log10(max(0, 1e-10)) = log10(1e-10) = −10
log10(16) = 1.204
final frame = [ −10,  1.204,  −10 ]

That three-number vector is one column of the spectrogram. Scale it up — 400 samples, 201 bins, 128 bands, one column every 10 ms — and it is exactly what Qwen2.5-Omni's audio encoder receives. The arithmetic does not change; only the sizes do.

The log matters, by the way, and for the same reason the mel warp does: loudness perception is logarithmic. A sound ten times more powerful is heard as roughly one step louder, not ten. The log makes the numeric scale match the perceptual one, which is what lets a neural network treat differences in the array as differences in what a listener would notice.

Inline check. If the tone had been at 3 Hz instead of 2 Hz, which mel band would light up? Bin 3 carries the energy; from the filterbank, bin 3 contributes 0.5 to mel 1 and 0.5 to mel 2. So the energy splits between two bands. That splitting is what overlapping triangular filters are for — a frequency between two band centres produces a smooth blend rather than a hard jump, so small pitch changes produce small vector changes.

With the counts and one full frame's values in hand, one question remains about the audio path: why these particular frequency bands?

Why "mel", and why 128 of them

We have used the word "mel" three times without defining it. Fix that now, because the choice of 128 is one of the paper's inherited-but-consequential decisions.

Human hearing is not linear in frequency. The difference between 100 Hz and 200 Hz is enormous perceptually — an octave, a completely different pitch. The difference between 8,000 Hz and 8,100 Hz is inaudible. The mel scale is a warping of frequency designed so that equal steps in mel correspond to equal steps in perceived pitch. The standard formula:

m(f) = 2595 · log10( 1 + f / 700 )

Work it by hand for the endpoints of our band, so the compression is visible:

f (Hz)1 + f/700log10m = 2595 × log10
01.0000.00000.0
1001.1430.0580150.5
2001.2860.1092283.4
10002.4290.38541000.0
40006.7140.82712146.1
800012.4291.09452840.2

Read the last column. The first 200 Hz of real frequency occupies 283 mel — ten percent of the whole scale. The last 4,000 Hz (from 4 kHz to 8 kHz) occupies only 694 mel. Low frequencies, where vowels and pitch live, get vastly more resolution than high frequencies, where only sibilance and noise live. That is exactly how your cochlea allocates hair cells.

So what does a mel filterbank do? It takes the linear-frequency power spectrum from the FFT — for a 400-sample window that is 201 frequency bins — and sums it into 128 overlapping triangular bands whose centres are equally spaced in mel. In matrix terms:

mel_frame (128,) = filterbank (128, 201) × power_spectrum (201,)

Compression from 201 numbers to 128, allocated where hearing actually is.

And why 128 rather than 80? Because Whisper-large-v3 uses 128, and this encoder is initialized from Whisper-large-v3. Earlier Whisper versions used 80, the long-standing default in speech recognition. The v3 bump to 128 buys finer spectral resolution, which matters more once the model is asked to do things beyond transcription — identifying instruments, classifying vocal sounds, judging musical tempo. Qwen2.5-Omni is asked to do all of those (Chapter 9 has the VocalSound and GiantSteps numbers), so it inherits the higher-resolution front end and benefits from it.

The inheritance chain is the design. 16 kHz, 128 mels, 25 ms, 10 ms — not one of these numbers was chosen by this paper. They are Whisper's, and they are here because the audio encoder is Whisper-large-v3's weights. This is worth noticing as a pattern: in a systems paper, most "hyperparameters" are actually compatibility constraints inherited from whatever checkpoint you started from. The genuinely new number in this front end is the pooling that produces 40 ms frames, and it exists because 40 ms is a convenient tick for aligning with video.
One more thing the 128 buys. Chapter 9 reports GiantSteps tempo estimation at 0.88 and MusicCaps captioning above LP-MusicCaps on several metrics — music tasks, on a model whose audio front end came from a speech recognizer. Musical timbre lives in the harmonic structure above 4 kHz, precisely the region an 80-band filterbank smears together. The extra 48 bands are part of why an "omni" model can be asked about music at all, rather than only about speech.

Frozen or trained? Reading the encoder stack

A question worth asking of any composed system, and the answer is staged (full detail in Chapter 8):

ComponentStage 1Stage 2–3Why
Mel front endFixedFixedIt is deterministic signal processing, not learned parameters. There is nothing to train.
Audio encoder (Whisper init)Trained (adapter first)TrainedWhisper's features were optimized for transcription; the LLM wants features for understanding.
Vision encoder (Qwen2.5-VL init)Trained (adapter first)TrainedSame argument, plus it must learn the video-with-audio regime.
LLMFrozenTrainedProtect an expensive model from the noise gradients of an unaligned encoder.

The row that teaches the most is the first. The mel transform has no parameters, and yet it is doing an enormous amount of work — a 640× compression with a perceptually motivated frequency warp, entirely by hand-designed signal processing. Even in 2025, the very first stage of the most modern omni model is a piece of 1930s psychoacoustics.

Inline check before the budget arithmetic. Which is cheaper for the LLM: ten seconds of speech, or one 448-pixel image? Speech: 10 × 25 = 250 tokens. One image: 256 tokens per frame, and an image is two identical frames, so 512. The still image costs twice as much as ten seconds of talking. Hold that ratio; it explains why nothing in this architecture is optimized for audio cost.

Putting the rates together: a one-minute call

Make it concrete with a realistic input, because this is the arithmetic that decides whether a design is deployable.

QuantityComputationResult
Audio tokens60 s × 251,500
Video frames at 2 fps60 × 2120
Visual tokens at 448 px120 × 25630,720
Total1,500 + 30,72032,220
Context limit32,768

One minute of 448-pixel video at two frames per second essentially fills the entire 32k context. That is not a coincidence and it is not a comfortable margin — it is the design point. It also explains the dynamic frame rate as something other than a nicety: drop to 1 fps and you halve the cost; drop the resolution to 224 px and you quarter it again. Those knobs are what keep the model usable on real video, and Chapter 7 lets you turn them.

Inline check. If you wanted to process a five-minute video within 32k tokens, what would you have to do? At 224 px (64 tokens per frame) and 0.5 fps you get 300 s × 0.5 × 64 = 9,600 visual tokens plus 7,500 audio tokens = 17,100. It fits — at the cost of seeing one frame every two seconds and a quarter of the spatial detail. There is no free version of this trade, which is why "dynamic frame rate" is a first-class part of the architecture rather than a preprocessing detail.
The mel-spectrogram has a 10 ms hop, giving 100 frames per second. Why does each audio token the LLM sees cover 40 ms instead?
A 448×448 video frame goes through patch-14 embedding and the 2×2 MLP merge. How many tokens reach the LLM?

Chapter 3: TMRoPE — Putting Everything on One Clock

We now have three streams of tokens with three different rates. Audio ticks 25 times a second. Video ticks at whatever frame rate we chose. Text does not tick at all. They are about to be poured into one sequence and fed to one attention mechanism, and the attention mechanism has no idea what time it is.

That is the problem TMRoPE solves. It is the paper's one genuinely novel piece of mathematics, and it is small enough to derive completely.

Step back: what does a position embedding actually do?

Self-attention is permutation invariant. Shuffle the tokens and the attention scores shuffle with them — the mechanism itself cannot tell "the cat sat" from "sat the cat". Order has to be injected.

Rotary Position Embedding (RoPE) injects it by rotating. Split each query and key vector into consecutive pairs of coordinates. Treat each pair as a point in a 2-D plane. Then, for a token at position m, rotate pair i by the angle m · θi, where θi is a fixed frequency assigned to that pair.

Rotate the pair (x2i, x2i+1) by angle α = m · θi:
x'2i  = x2i cosα − x2i+1 sinα
x'2i+1 = x2i sinα + x2i+1 cosα
with   θi = base−2i/d,   base = 10,000,   d = head dimension.

Why rotation? Because of one beautiful property. The attention score between a query at position m and a key at position n is a dot product, and rotating both by their own angles makes that dot product depend only on m − n:

⟨ R(mθ) q,   R(nθ) k ⟩ = ⟨ q,   R((n − m)θ) k ⟩

Absolute positions go in; a relative position comes out. The model never has to learn "position 4,127" as a special thing; it only ever perceives distances. That is why RoPE extrapolates gracefully and why it took over the field.

The frequency ladder is the trick within the trick. With base 10,000 the frequencies θi span many orders of magnitude — the first pair rotates a full radian per token, the last pair barely moves over thousands of tokens. So the pairs act like the hands of a clock: fast hands resolve neighbouring tokens, slow hands encode coarse position in the document. One vector carries position at every scale simultaneously.

From 1-D to 3-D: M-RoPE

Plain RoPE assumes position is a single number. For an image that is wrong — a patch has a row and a column, and flattening them into one index destroys the two-dimensional structure. Qwen2-VL's answer, M-RoPE, is to split the rotation budget three ways.

Instead of one position m, every token carries a triple: (t, h, w) — temporal, height, width. The head dimension is partitioned into three groups of frequency pairs; group T is rotated by t · θ, group H by h · θ, group W by w · θ. The relative-position property still holds, independently, on each axis.

ModalityHow the triple is assignedEffect
TextAll three components identical: token i gets (i, i, i)M-RoPE degenerates exactly to 1-D RoPE — nothing is lost for pure text
Imaget is constant for every token of the image; h = row index, w = column index (offset by a start id)Two patches in the same row differ only in w; the model perceives genuine 2-D geometry
VideoA series of images: t increments per frame; h and w assigned exactly as for imagesThree real axes — when, where vertically, where horizontally
Audio (TMRoPE's addition)All three identical again, like text — but anchored to absolute time: one temporal ID = 40 msAudio tokens now live on the same time axis as video frames

That last row is TMRoPE. The report defines it as "Multimodal Rotary Position Embedding (M-RoPE) with absolute temporal positions". Everything else was already in Qwen2-VL. The contribution is: stop letting the temporal index mean "the n-th thing in the sequence", and make it mean "the moment at time n × 40 ms".

Why that one change is load-bearing. Suppose video is sampled at 2 fps and audio at 25 tokens/s. Under sequence-index positions, the fifth video token and the fifth audio token get the same position — but they are 2.5 seconds and 0.2 seconds into the clip respectively. The model's notion of "at the same time" would be wildly wrong, and worse, it would be wrong differently for every frame rate. Anchoring to absolute time makes "same position ID" mean "same instant", for every modality, at every frame rate. The clap and the hands meeting land on the same number.

Dynamic frame rate: how video gets onto the 40 ms grid

Video frame rates are not fixed and generally are not multiples of 25 fps. The report handles this directly: "Since the frame rate in video is not fixed, we dynamically adjust the temporal IDs between frames based on the actual time corresponding to each frame to ensure that one temporal ID corresponds to 40ms."

So the rule for a video frame displayed at time τ seconds after the start of the visual block is:

temporal ID = start_id + floor( τ / 0.040 )

The temporal IDs of successive frames are therefore not consecutive integers. At 2.5 fps they step by 10. At 1 fps they step by 25. At 25 fps they step by 1. The gaps encode the frame rate, which means the model can tell a slideshow from a fast pan without being told.

Modality chaining: where each block starts

One more rule, quoted exactly: "In scenarios where the model's input encompasses multiple modalities, position numbering for each modality is initialized by incrementing the maximum position ID of the preceding modality by one."

Note carefully: the maximum, across all three components, of everything that came before — plus one. Not the count of tokens. This matters because a modality's IDs can be sparse (a 4-second video occupies 100 temporal IDs but may contribute only 10 frames) and because the h and w components can run ahead of t for a tall image.

The fully worked position-ID example

Now we compute a complete assignment by hand. Nothing is skipped. Take the input:

The input. Three text tokens ("Describe", "the", "clip"), then a 4-second video-with-audio at 112×112 pixels sampled at 2.5 frames per second, then one more text token (".").

Part A — the text prefix. Text uses identical components. Token i gets (i, i, i):

Token(t, h, w)
"Describe"(0, 0, 0)
"the"(1, 1, 1)
"clip"(2, 2, 2)

Maximum position ID so far = 2. Therefore the video-with-audio block starts at st = 2 + 1 = 3.

Part B — how many tokens of each kind? From Chapter 2's arithmetic:

Part C — audio temporal IDs. Audio token j covers the interval [0.040j, 0.040(j+1)) seconds. Identical components, anchored:

audio token j → (t, h, w) = (st + j,   st + j,   st + j) = (3 + j, 3 + j, 3 + j)

So j = 0 gives (3, 3, 3); j = 30 gives (33, 33, 33); j = 99 gives (102, 102, 102). Maximum audio ID = 102.

Part D — video temporal IDs. Frame k sits at τk = 0.4k seconds. Convert to temporal ID:

tk = st + floor(τk / 0.040) = 3 + floor(0.4k / 0.04) = 3 + 10k
Frame kTime τkτk / 0.04Temporal ID tk
00.0 s03
10.4 s1013
20.8 s2023
31.2 s3033
41.6 s4043
93.6 s9093

Why floor rather than round? Because floor means "the 40 ms tick that contains this frame". That makes the alignment exact at every frame rate: whichever audio token covers the same instant carries the same number, with no off-by-one at rates that do not divide 25 evenly. At 2.5 fps the two conventions agree anyway — 0.4k / 0.04 is exactly 10k — which is why the example below is clean either way.

Part E — the alignment check, which is the whole point. Look at frame k = 3: temporal ID 33. Now find the audio token covering 1.2 s: j = 1.2 / 0.04 = 30, giving temporal ID 3 + 30 = 33. Identical.

Two tokens of completely different modalities, arriving at completely different points in the flattened sequence, carry the same temporal position ID because they describe the same 40 ms of the world. When attention computes their relative temporal rotation it gets zero. They are, to the model, simultaneous.

Part F — spatial components for video. Frame k, grid cell (r, c):

(t, h, w) = ( 3 + 10k,   3 + r,   3 + c )

So the token at frame 3, row 2, column 1 is (33, 5, 4). Its neighbour to the right is (33, 5, 5); the one below is (33, 6, 4); the same cell one frame later is (43, 5, 4). Every relationship the model might want — adjacency in space, adjacency in time — is a small difference in exactly one component.

Part G — the trailing text token. Maximum position ID anywhere in the block: audio reached 102; video reached t = 93 and h = w = 6. So max = 102, and the next text token starts at 103, receiving (103, 103, 103).

Sanity-check the result. The block spans 4 real seconds and consumed 100 temporal IDs (3 through 102). 100 × 40 ms = 4.00 s. ✓ The position space is time, measured in 40 ms ticks, not a token counter — even though 260 tokens were emitted. If you doubled the frame rate to 5 fps you would get 20 frames and 320 tokens, but the temporal span would still be exactly 3 to 102. That decoupling of "how much I looked" from "how long it lasted" is the property TMRoPE exists to provide.

Now the rotation itself, with actual numbers

Position IDs are only half the story; they have to become rotations. Take a toy head dimension of d = 8, giving 4 frequency pairs. The frequencies:

θi = 10000−2i/8 = 10000−i/4
θ0 = 100000 = 1  ·  θ1 = 10000−0.25 = 1/10 = 0.1
θ2 = 10000−0.5 = 1/100 = 0.01  ·  θ3 = 10000−0.75 = 1/1000 = 0.001

M-RoPE partitions those 4 pairs among the three axes. Real configurations give temporal the low-frequency (long-range) pairs; for our toy, allocate pairs {0, 1} to t, pair {2} to h, pair {3} to w.

Take our token from Part F: (t, h, w) = (33, 5, 4). The four rotation angles are:

PairAxisθIDAngle = ID × θcossin
0t13333.000 rad−0.0132770.999912
1t0.1333.300 rad−0.987480−0.157746
2h0.0150.050 rad0.9987500.049979
3w0.00140.004 rad0.9999920.004000

The 33-radian angle is worth checking by hand, because it shows the wrap-around that makes the fast pairs work as fine-grained hands: 33 − 10π = 33 − 31.415927 = 1.584073 rad, which is just past a quarter turn — hence cos near zero and sin near one.

Apply them to a query vector q = (1, 0,  1, 0,  1, 0,  1, 0), pair by pair, using the rotation formulas above (each pair has x2i = 1, x2i+1 = 0, so x' = cosα and x'' = sinα):

q' = ( −0.013277, 0.999912,   −0.987480, −0.157746,   0.998750, 0.049979,   0.999992, 0.004000 )

Now the payoff computation. Take the audio token j = 30, whose triple is (33, 33, 33), and give it the same raw vector. Its angles: pair 0 → 33 × 1 = 33 (same as the video token), pair 1 → 3.3 (same), pair 2 → 33 × 0.01 = 0.33, pair 3 → 33 × 0.001 = 0.033.

The relative angles between the video token and the audio token are therefore:

temporal pairs: 33 − 33 = 0 and 3.3 − 3.3 = 0  →  no temporal phase difference at all
h pair: 0.33 − 0.05 = 0.28  ·  w pair: 0.033 − 0.004 = 0.029

The attention mechanism sees these two tokens as temporally coincident, differing only along the spatial axes (which is correct: audio has no meaningful spatial position, and it sits at a spatial "origin" defined by the block start). That is the entire mechanism, in numbers you just checked.

Compare with what plain sequence-index RoPE would have produced. The video token is the 3×16 + 2×4 + 1 = 57th visual token in the block, the audio token is the 160 + 30 = 190th token in the block — a relative distance of 133 positions. The model would perceive the clap and the hands meeting as separated by 133 tokens' worth of phase. TMRoPE turns 133 into 0.

Build the position table yourself

TMRoPE assigner — drag the clock and watch the IDs

Three lanes on one real-time axis: text, video frames, audio frames. The vertical cursor is a moment in time; tokens under it share a temporal ID (shown in the readout). Switch to Sequence IDs to see what happens without TMRoPE — the same cursor now lands on tokens with wildly different position numbers.

Code: the assignment, three ways

First, by hand for one token — the arithmetic you just did:

python — one token, by hand
st   = 3            # max position id of the text prefix, plus one
k, r, c = 3, 2, 1    # video frame 3, grid row 2, column 1
fps  = 2.5
tau  = k / fps                      # 1.2 seconds
t_id = st + int(tau / 0.040)       # 3 + 30 = 33  (floor: the tick containing tau)
h_id = st + r                       # 5
w_id = st + c                       # 4
print(t_id, h_id, w_id)              # 33 5 4

Then the whole block, step by step in numpy — this is the function you would actually write:

python — the full TMRoPE position table
import numpy as np
TICK = 0.040                   # one temporal ID = 40 ms. The whole paper.

def tmrope_block(st, secs, fps, grid, n_audio_tok):
    """Return (N,3) int array of (t,h,w) ids, and the new max id."""
    ids = []
    # --- visual tokens: t from wall-clock time, h/w from the merged grid
    n_frames = int(round(secs * fps))
    for k in range(n_frames):
        t = st + int((k / fps) / TICK)
        for r in range(grid):
            for c in range(grid):
                ids.append((t, st + r, st + c))
    # --- audio tokens: identical components, one id per 40 ms
    for j in range(n_audio_tok):
        ids.append((st + j, st + j, st + j))
    ids = np.array(ids, dtype=np.int64)
    return ids, int(ids.max()) + 1      # next modality starts here

ids, nxt = tmrope_block(st=3, secs=4, fps=2.5, grid=4, n_audio_tok=100)
print(ids.shape, nxt)               # (260, 3) 103   <- matches Part G
print(ids[3*16 + 2*4 + 1])          # [33  5  4]   <- matches Part F
print(ids[160 + 30])                # [33 33 33]   <- same instant!

One simplification to flag: that function emits all visual tokens and then all audio tokens. The real arrangement chops the block into 2-second chunks and alternates — which changes the sequence order but not a single position ID. Chapter 4 does exactly that, and you will be able to check that the IDs are unchanged.

And the rotation, applied with the three-way split. This is the shape every M-RoPE implementation has, including the one in transformers:

python — applying the three-way rotary split
def mrope(x, ids, mrope_section):
    """x: (N, d) queries or keys. ids: (N,3). mrope_section: pairs per axis."""
    d = x.shape[-1]
    i = np.arange(d // 2)
    theta = 10000.0 ** (-2 * i / d)          # (d/2,)  the frequency ladder

    axis = np.repeat([0, 1, 2], mrope_section)  # which id feeds which pair
    pos   = ids[:, axis]                        # (N, d/2)
    ang   = pos * theta                         # (N, d/2)  ID x frequency

    a, b = x[:, 0::2], x[:, 1::2]
    out = np.empty_like(x)
    out[:, 0::2] = a * np.cos(ang) - b * np.sin(ang)
    out[:, 1::2] = a * np.sin(ang) + b * np.cos(ang)
    return out

q  = np.array([[1,0,1,0,1,0,1,0]], dtype=float)
print(mrope(q, np.array([[33,5,4]]), mrope_section=[2,1,1]))
# [[-0.013277 0.999912 -0.987480 -0.157746 0.998750 0.049979 0.999992 0.004]]
# exactly the table above — you can check every entry by hand.

And the one-liner, for completeness — in practice you never write this yourself:

python — the library call
from transformers import Qwen2_5OmniForConditionalGeneration, AutoProcessor
m = Qwen2_5OmniForConditionalGeneration.from_pretrained("Qwen/Qwen2.5-Omni-7B")
# the processor builds the (3, B, T) position_ids itself from the
# interleaved inputs; rope_scaling["mrope_section"] holds the split.
out = m.generate(**proc(text=prompt, audio=wav, videos=frames, return_tensors="pt"))
What the paper does not spell out, and where this lesson filled gaps. The report defines TMRoPE's rule (three components, 40 ms per temporal ID, max-plus-one chaining) but not the exact frequency split across the three axes, nor whether the offset applied to h and w is the block start or something else. The conventions used above are the standard M-RoPE ones inherited from Qwen2-VL, and they reproduce every published statement. If you are implementing against the real checkpoint, read rope_scaling["mrope_section"] from the config rather than trusting the toy split used here.
Before the failure modes, the one-sentence summary. TMRoPE replaces "position means how far along the sequence you are" with "position means when in the world this happened" — and because attention only ever perceives differences of position, that single substitution makes simultaneity across modalities a thing the model can feel directly, at every frame rate, for free.

Five ways to get TMRoPE wrong

Position bookkeeping is the kind of code that fails silently: nothing crashes, the loss goes down, and the model just never learns to relate sound to sight. Here are the failure modes, each with the symptom it produces.

MistakeSymptomWhy it is tempting
Using the token index as the temporal IDAlignment is wrong by an amount that depends on frame rate; the model learns nothing transferableIt is what every ordinary transformer does
Starting each modality at 0A text token and an audio token both sit at "position 5" and appear simultaneous when they are notFeels tidy; each modality gets clean numbering
Chaining by token count instead of max IDOff-by-many drift after any sparse modality (a 4 s video occupies 100 IDs but may contribute only 10 frames)The count is the number you already have in hand
Forgetting that h and w can exceed tThe next modality overlaps the previous one's spatial IDs on a tall imageYou think of "position" as one number
Rounding rather than flooring the frame timeAt frame rates that do not divide 25, a frame drifts one tick away from the audio token containing itRounding feels more accurate

The unifying diagnosis: every one of these comes from thinking of position as "how far along the sequence am I" rather than "when did this happen". Once the second framing is internalized, the rules stop needing to be memorized — you can re-derive each one by asking what time the token describes.

A test you can run on your own implementation. Take a video-with-audio clip. For every video frame, find the audio token whose 40 ms window contains that frame's timestamp, and assert their temporal IDs are equal. Then re-chunk the sequence with a different chunk size and assert every ID is unchanged. Those two assertions catch four of the five mistakes above, and they run in milliseconds.
A video is sampled at 5 fps. Under TMRoPE, by how much do the temporal position IDs increase from one frame to the next?
In the worked example the video block began at start id 3 even though the text prefix had 3 tokens with ids 0, 1, 2. What is the general rule?

Chapter 4: The Two-Second Interleave

TMRoPE told the model when each token happened. It did not tell the model in what order to receive them. Those are different problems, and the second one only exists because of a constraint we have not yet made explicit: a causal decoder can only attend backwards.

This chapter is short in the paper — four sentences — and consequential out of all proportion to its length.

Manufacturing the need

Suppose you take the naive route from Chapter 3's code: put all 160 visual tokens first, then all 100 audio tokens. Now ask what the model can do.

The audio token at 0.2 seconds sits at sequence index 165. It can attend backwards to every video frame, including frames from 3.6 seconds — the future. Fine for offline understanding, fatal for streaming: the model cannot be run until the entire clip has been seen, because the very first audio token's context requires the last video frame to exist.

Flip it — audio first, then video — and the symmetric problem appears: the video frame at 0.0 s can attend to audio from 3.96 s, and again nothing can be computed until the clip ends.

What about strict time order — sort every token by its timestamp and interleave at the finest granularity? Now the ordering is causal and streaming works. But you have created a different problem: within each 40 ms tick you must interleave 16 visual tokens with 1 audio token, and you have shattered each video frame's spatial block into fragments separated by audio. Attention within a frame now has to reach across interruptions, and the KV-cache layout becomes a mess. Worse, at high frame rates the pattern changes shape entirely.

The tension, stated cleanly. Fine-grained interleaving gives you tight causality but destroys the contiguity of each modality's blocks. Coarse-grained ordering (all of one, then all of the other) preserves contiguity but destroys causality. The design space is a single knob — the chunk size — and every value trades one against the other. The paper picks a value and moves on; this chapter is about why that value.

The rule

Quoting exactly: "we have a special design for video with audio called the time-interleaving method, which segments the representation in the video with audio into chunks every 2 seconds according to the actual time. We then arrange the visual representation at the front and the audio representation at the back within the 2 seconds, interleaving the representations of the video with audio."

Three decisions in one sentence:

  1. Chunk by real time, not by token count. Two seconds of clock, whatever number of tokens that turns out to be. At 2.5 fps it is 5 frames; at 10 fps it is 20. The chunk boundary is a wall-clock boundary, so it stays aligned with the audio encoder's 2-second attention blocks (Chapter 6). These two "2 second"s are the same 2 seconds.
  2. Visual first, audio second, within each chunk. Not alphabetical, not arbitrary — and Chapter 6 explains the payoff.
  3. Alternate chunks. V1 A1 V2 A2 V3 A3

Applied to our 4-second worked example, the sequence order becomes:

SegmentContentsCountTemporal IDs present
V1Frames k = 0…4 (times 0.0–1.6 s), 16 tokens each803, 13, 23, 33, 43
A1Audio j = 0…49 (times 0.00–2.00 s)503 … 52
V2Frames k = 5…9 (times 2.0–3.6 s), 16 tokens each8053, 63, 73, 83, 93
A2Audio j = 50…99 (times 2.00–4.00 s)5053 … 102

Check the invariant promised at the end of Chapter 3: not one temporal ID moved. Frame 3 is still 33; audio token 30 is still 33. The positions encode time; the order encodes causality. Separating those two jobs is the reason both can be satisfied at once, and it is worth pausing on because most sequence models conflate them completely.

The insight in one line. In a standard transformer, sequence order and position are the same thing. TMRoPE plus time-interleaving pries them apart: order determines what may attend to what; position determines how far apart things feel. Once separated, you can make order serve streaming and position serve alignment, and neither has to compromise.

The layout is fixed. Now the two questions any reader should be asking: what does the chunk size cost, and why is the audio placed after the video rather than before it?

What does the 2 seconds actually cost?

Define staleness: for a given token, how far into the future (in real time) does some token that precedes it in the sequence reach?

Inside chunk 1, the audio token at 0.00 s sits after the video frame at 1.6 s. So that audio token can attend to visual evidence from 1.6 seconds in its own future. That is an acausality of up to one chunk — bounded, and bounded by exactly the chunk size.

max acausality = chunk size = 2 seconds
max streaming delay before chunk n can be processed = chunk size = 2 seconds

Both terms are the chunk size, which is why the knob is a straight trade. Shrink to 0.5 s and you get a 0.5 s streaming delay and only 0.5 s of cross-modal lookahead; grow to 8 s and you get rich context and an 8 s delay. The paper's 2 seconds is a claim that two seconds of audio-visual context is roughly what you need to relate a gesture to a word, and that two seconds of buffering is roughly what a conversational system can absorb before it feels sluggish.

Notice, too, that within a chunk the acausality is not a bug that has to be hidden. It is exactly the property that lets a spoken "this" be interpreted using a gesture that occurred slightly later — something humans do constantly. A strictly causal ordering would forbid it.

Why visual first?

The paper does not say. Here is the reasoning that makes it the obviously right choice, offered as reconstruction rather than quotation.

Because audio is the modality that must be answered. In a voice interaction the instruction arrives as speech. The tokens closest to the model's generation point — the ones with the shortest attention path and, empirically, the strongest influence — should be the ones carrying the request. Putting audio at the back of each chunk means the last thing before the prompt continues is what was said.

Because audio should see the visuals of its own chunk. A causal decoder lets audio attend backwards to the video in the same chunk, but not vice versa. If you want "what does this refer to" resolved inside the audio representations, audio must come after the pixels. Reversing the order would force the resolution to happen later, higher in the stack, with a longer path.

Because visual tokens are the bulky, contiguous ones. Keeping all of a chunk's frames adjacent preserves the block structure the vision encoder produced and keeps the KV-cache tidy for the flash-attention paths.

How to test this reconstruction. Swap the order in the sim below and reason about what breaks: with audio first, an audio token cannot attend to any video frame from its own two seconds, so any deictic reference ("this one", "that colour") must be resolved across a chunk boundary — two seconds late. That single consequence is enough to explain the choice.

Before the numbers, feel the mechanism. The sim below shows both strips at once — the world's timeline and the model's sequence — and lets you drag the boundary between them.

Build the interleave

Chunk builder — real time on top, sequence order below

The upper strip is wall-clock: video frames as tall ticks, audio as a continuous band. The lower strip is the flattened sequence the LLM actually receives. Drag the chunk size and watch the sequence reorganize; the dashed link shows where one particular moment in time ends up in the sequence.

Push the chunk size to its minimum and watch the sequence become almost perfectly time-ordered — and watch the video frames get chopped into isolated islands. Push it to maximum and you recover the all-video-then-all-audio layout with its four-second delay. Two seconds sits deliberately in the middle.

One thing left to make fully concrete: the code, and the assertion that proves the invariant.

Code: the chunked arrangement

python — time-interleaving, and the ID invariant
import numpy as np
TICK, CHUNK = 0.040, 2.0          # 40 ms per temporal id; 2 s per chunk

def interleave(st, secs, fps, grid, chunk=CHUNK):
    seq, ids = [], []
    n_chunks = int(np.ceil(secs / chunk))
    for ci in range(n_chunks):
        t0, t1 = ci * chunk, min((ci + 1) * chunk, secs)
        # ---- visual first ----
        k = int(np.ceil(t0 * fps))
        while k / fps < t1:
            t = st + int((k / fps) / TICK)             # SAME formula as Ch 3
            for r in range(grid):
                for c in range(grid):
                    seq.append(("V", k, r, c)); ids.append((t, st + r, st + c))
            k += 1
        # ---- audio second ----
        for j in range(int(t0 / TICK), int(t1 / TICK)):
            seq.append(("A", j)); ids.append((st + j, st + j, st + j))
    return seq, np.array(ids)

seq, ids = interleave(st=3, secs=4, fps=2.5, grid=4)
print(len(seq), ids.max())                       # 260 102  — unchanged

# the invariant: frame 3 and audio token 30 both carry temporal id 33,
# even though they now sit in different halves of the sequence.
i_v = next(i for i,s in enumerate(seq) if s == ("V", 3, 2, 1))
i_a = next(i for i,s in enumerate(seq) if s == ("A", 30))
print(i_v, i_a, ids[i_v][0], ids[i_a][0])       # 57 110 33 33

Run that mentally: the sequence indices changed from 57 and 190 to 57 and 110, because audio token 30 moved from the back of the whole block to the back of chunk 1. The temporal IDs did not move at all. Order and position, doing separate jobs.

What the ChatML wrapper looks like

All of this lives inside an ordinary chat template. The report gives the format, and it is worth reading because it shows how modality boundaries are marked and how video-with-audio is described in training data:

ChatML — the paper's own dataset format example
<|im_start|>user
<|vision_start|>Video.mp4 [Two people are talking in the video]<|vision_end|>What are the
people in the video saying?<|im_end|>
<|im_start|>assistant
Both pictures are of SpongeBob SquarePants. The person in the red clothes says, "Hello,
how's the weather today?" The person in the black clothes responds, "Hello, the weather
is quite nice today."<|im_end|>

Notice: the special tokens delimit the visual span, the video's audio content is part of what the assistant is expected to report, and the whole thing is just a conversation. Modality plumbing is invisible at this layer — which is the point of doing the position bookkeeping properly underneath.

Before the second example, restate the invariant one more time, because everything below is a test of it: chunking changes the order of tokens and nothing else. Position IDs come from wall-clock time; sequence indices come from the chunking. Any implementation where the two are entangled will pass the easy case and fail the ragged one.

So the exercise that follows is deliberately awkward: a clip length that is not a multiple of the chunk size, and a frame rate that does not divide the 40 ms tick rate. If the rule survives both, it is the right rule.

A second worked example, end to end

The 4-second clip divided suspiciously evenly. Do a messier one, because the messy cases are where an implementation reveals whether it understood the rule.

The input. Five text tokens of instruction, then a 7-second video-with-audio at 3 fps, 224×224 pixels, chunked at 2 seconds.

Counts first.

QuantityComputationResult
Start idtext ids 0..4, max 4, plus 1st = 5
Audio tokens7 / 0.040175
Video frames3 × 721
Tokens per frame224/14 = 16 per side; 16×16 = 256 patches; 2×2 merge → 8×864
Visual tokens21 × 641,344
Chunksceil(7 / 2)4 — the last one is short

Now the chunk table. Frames land at k/3 seconds for k = 0..20. Audio token j covers [0.04j, 0.04(j+1)).

ChunkTime spanFrames kVisual tokensAudio jAudio tokens
10.00–2.00 s0–5 (6 frames)3840–4950
22.00–4.00 s6–11 (6 frames)38450–9950
34.00–6.00 s12–17 (6 frames)384100–14950
46.00–7.00 s18–20 (3 frames)192150–17425

Check the frame counts. Chunk 1 holds frames with k/3 < 2, i.e. k < 6, so k = 0..5 — six frames. ✓ Chunk 4 holds 6 ≤ k/3 < 7, i.e. 18 ≤ k < 21 — three frames. ✓ Totals: 6+6+6+3 = 21 frames ✓ and 50+50+50+25 = 175 audio tokens ✓.

Sequence order.

V1(384) A1(50) V2(384) A2(50) V3(384) A3(50) V4(192) A4(25)
total in the block = 1,344 + 175 = 1,519 tokens

And the position IDs, which the chunking did not touch. Frame k: t = 5 + floor((k/3) / 0.040). Spot-check a few:

Frame kTime(k/3)/0.04floorTemporal IDAudio token at that instantIts ID
00.000 s0.0005j = 05 ✓
10.333 s8.33813j = 8 (0.32–0.36 s)13 ✓
72.333 s58.335863j = 58 (2.32–2.36 s)63 ✓
206.667 s166.67166171j = 166 (6.64–6.68 s)171 ✓

Every row matches, at a frame rate (3 fps) that does not divide evenly into 25 — which is exactly the case the floor convention was chosen to handle. Had we rounded, frame 1 at 8.33 would still floor and round to 8, but a frame at, say, 0.6667 s would give 16.67, rounding to 17 while the containing audio token is 16. One tick of drift, silently, on some frames and not others.

Maximum ID and what comes next. Audio reaches 5 + 174 = 179; video reaches t = 171 and h = w = 5 + 7 = 12. So max = 179, and the next text token starts at 180. Sanity: 7 seconds ÷ 0.040 = 175 temporal ticks, occupying IDs 5 through 179. ✓

Notice what did not have to be special-cased. A ragged final chunk, a frame rate that does not divide the tick rate, a clip that is not a whole number of chunks. None of them needed a branch, because every quantity was derived from wall-clock time rather than from counting tokens. That property is the payoff of Chapter 3's design, and it is why the sequence layout can change freely without touching the position code.
Why the report spends four sentences on this. Time-interleaving is the least glamorous idea in the paper and arguably the one with the highest ratio of consequence to page count. It is what makes the sequence emittable incrementally, which is what makes chunked prefill legal, which is what moves the encoding and prefill costs off the critical path (Ch 7). A reader skimming for novelty would skip it. A reader building the system would spend a week on it.

The rule is stated and tested. Now price it — first as a curve over chunk sizes, then as a memory-layout argument, then as the coupling that makes the whole pipeline work.

What the chunk size costs, at four settings

The knob has one setting in the paper and a whole curve behind it. Here is the curve, computed for our 4-second clip at 2.5 fps with 16 tokens per frame.

ChunkChunksSequence layoutStreaming delayCross-modal lookaheadFrames per visual segment
0.5 s8V A V A V A V A V A V A V A V A0.5 s — excellent0.5 s — can barely relate a gesture to a word1 — blocks shattered
1.0 s4V A × 41.0 s1.0 s2–3
2.0 s2V A V A2.0 s — the paper2.0 s — a whole gesture fits5
4.0 s1V A4.0 s — the whole clip4.0 s10 — maximum contiguity

Notice that the delay column and the lookahead column are the same number, because they are the same quantity read from two directions. You cannot buy more cross-modal context without buying more waiting. That is the trade in its purest form, and it is why picking a chunk size is a product decision rather than a research one.

Two seconds is a defensible middle. Human conversational turn-taking gaps average roughly 200 ms, so two seconds of buffering would be fatal if it were on every turn — but it is not. It is the granularity at which input chunks become available, and the pipeline overlaps that with everything downstream (Chapter 7). Meanwhile two seconds comfortably contains a pointing gesture, a head turn, or a full spoken clause — the units of meaning that need to be related across modalities.

Three consequences remain: what the sequence layout does to the KV cache, how the chunk boundary locks to the encoder's, and how all of this is wrapped in an ordinary chat template. The template comes first, because it is where the training signal for video-with-audio instruction following actually lives.

Reading the ChatML wrapper as structure

The template in the previous section is not decoration; the control tokens are load-bearing. Three jobs are being done at once.

MarkerJobWhat would break without it
<|im_start|>role / <|im_end|>Turn boundaries and speaker rolesThe model cannot tell instruction from response, or this turn from the last
<|vision_start|> / <|vision_end|>Delimit the span of visual tokens inside the text streamVisual tokens would be indistinguishable from text embeddings at the sequence level
The bracketed description inside the vision spanCarries what the audio track of the video contains, during trainingThe model would have no supervision linking the audio track to the response

That third row is easy to miss and quietly important. In the paper's own example the training item contains [A person in the video is saying, "Please describe the person in front of you."] — the instruction arrives inside the video's audio, and the assistant is expected to obey it. That is the training signal for video-with-audio instruction following, and it is what makes the OmniBench result in Chapter 9 possible.

The KV-cache view: why contiguity is not an aesthetic preference

An argument that only becomes visible once you think about implementation.

A frame's 16 (or 256) visual tokens are produced together by the vision encoder and they attend to each other heavily — that is what spatial structure means. When they sit contiguously in the sequence, their keys and values sit contiguously in the KV cache, and the attention over them is one dense tile that flash attention handles at peak efficiency.

Shatter them — interleave one audio token every 40 ms, as strict time ordering would — and at 2.5 fps you get roughly this pattern:

[16 visual] [10 audio] [16 visual] [10 audio] …

Now every frame's self-attention is a strided gather rather than a contiguous tile. Correctness is unaffected; throughput is not. And at 448 px, where a frame is 256 tokens, the interleave pattern changes shape entirely with frame rate, so no single kernel configuration is right.

The general lesson. Sequence layout is a performance interface, not just a semantic one. Any scheme that decides the order of tokens is implicitly deciding memory-access patterns for every attention kernel downstream. Two-second chunking is coarse enough to keep each modality's blocks intact and fine enough to keep the delay bounded — it is a layout decision as much as a modelling one.

Layout is settled. What remains is the coupling: why the interleave chunk and the encoder block are the same two seconds, and what breaks if you decouple them.

How this locks together with the rest of the stack

The 2 seconds appears three times in this paper, and they are the same 2 seconds. That is worth making explicit, because it is the kind of coupling that looks like a coincidence until you try to change one of them.

Audio encoder: attention in 2-second blocks
A block's tokens become final the moment the block is complete. Before that, nothing about them is fixed.
↓ therefore
Interleave: 2-second chunks
The chunk boundary is placed exactly where the tokens become final, so the sequence can be emitted as soon as it exists.
↓ therefore
Prefill: consumes complete chunks
Every prefilled token is final. Nothing has to be recomputed when more audio arrives.

Now imagine breaking the coupling: encoder blocks of 2 s but interleave chunks of 1 s. The first 1-second chunk would need audio tokens from a block that is only half-encoded. You would either stall (losing the benefit) or encode a partial block and re-encode it later (paying twice and risking a train-inference mismatch). Aligned boundaries are what make the pipeline a pipeline.

What happens at the edges

Two edge cases the report does not discuss but any implementation must:

A clip that is not a multiple of the chunk size. Our 4-second clip divides evenly into two chunks. A 4.7-second clip gives two full chunks and a 0.7-second remainder: 0.7 / 0.04 = 17 audio tokens and however many frames fall in that window. The remainder chunk is just shorter; nothing about the position rule changes, since IDs come from wall-clock time rather than from counting.

A chunk with no video frames. At 0.5 fps with 1-second chunks, half the chunks contain no frame at all. The visual segment is empty and the chunk is audio-only. Again nothing breaks — and this is precisely the case that would break a scheme based on fixed per-chunk token counts. Deriving structure from time rather than from counts is what makes the degenerate cases degenerate gracefully.

Inline check. A 10-second clip at 3 fps with 2-second chunks. How many frames in chunk 1? Frames land at 0, 0.333, 0.667, 1.0, 1.333, 1.667 seconds — six frames strictly before 2.0 s. And chunk 1's audio? 2.0 / 0.04 = 50 tokens. So the segment pair is V(6 × tokens-per-frame) then A(50), five times over. If you predicted "3 fps × 2 s = 6", you have the rule.
Under the 2-second time-interleaving scheme, what is the maximum amount of "future" real time that a token can attend backwards to?
Re-chunking a clip from 2-second chunks to 0.5-second chunks changes which of the following?

Chapter 5: Inside the Talker

Thinker is a normal LLM and you already understand it. Talker is the strange one. This chapter answers three questions: why it takes two different kinds of input, what it emits, and what "dual-track" means.

Why two inputs? The paper's own argument, unpacked

The report gives the reasoning in two sentences that repay slow reading. Here is the first:

"As a streaming algorithm, voice generation must anticipate the content's tone and attitude before the entire text is fully generated. The high-dimensional representations provided by Thinker implicitly convey this information, enabling a more natural streaming generation process."

Make it concrete. Thinker is about to produce: "Unfortunately, the capacitor is definitely blown." A streaming voice generator receiving only text sees the word "Unfortunately" and nothing else. How should it be said? Sympathetically? Briskly? The correct prosodic contour for the whole sentence depends on the whole sentence — which does not exist yet.

But Thinker's hidden state at the moment it emitted "Unfortunately" already encodes where the sentence is going, because that is how autoregressive planning works: the model computes a rich representation and then collapses it to one token. The token is the lossy part. The hidden state is the plan.

The hidden state is a leak from the future. That is the cleanest way to hold this. Text is a bottleneck that discards everything the model knew except which word it chose. Handing Talker the pre-bottleneck state gives it access to information that has not been said yet — the emotional register, the certainty level, the shape of the sentence to come. This is the same reason the cascade's TTS sounds subtly wrong: it is trying to reconstruct, from words, a plan that was thrown away one stage earlier.

Now the second sentence, which explains why hidden states are not enough:

"Furthermore, Thinker's representations primarily express semantic similarity in the representational space rather than phonetic similarity. Consequently, even phonetically distinct words may have very similar high-level representations, necessitating the input of sampled discrete tokens to eliminate such uncertainty."

This is a precise and slightly counterintuitive claim, so let us make it painfully concrete.

The claim needs examples before it will stick, and the examples are more surprising than the claim.

The semantic-phonetic mismatch, with examples

An LLM's representation space is organized by meaning. Words that mean similar things sit close together. But how a word sounds has essentially nothing to do with what it means — that is the arbitrariness of the sign, one of the founding observations of linguistics.

So the representation space is organized along an axis that is nearly orthogonal to the one the voice needs.

PairSemantic distance (what the hidden state sees)Phonetic distance (what Talker must produce)Failure without the discrete token
cat / dogTiny — both small domestic animalsLarge — no shared phonemesTalker may blur toward the wrong word entirely
eight / ateLarge — a number versus a verbZero — identicalHarmless here: either reading sounds right
read (present) / read (past)Small — same lemmaLarge — /riːd/ versus /rɛd/The token alone is ambiguous; here the hidden state is what disambiguates
colonelPronounced "kernel" — unpredictable from spellingNeeds the exact token identity; a semantic neighbourhood will not do

Row 1 is the killer argument. If Talker had only the hidden state, then at the moment Thinker's state says "small domestic pet", Talker faces a cloud of candidates — cat, dog, kitten, puppy — that are near-identical semantically and completely different acoustically. It cannot choose. The sampled discrete token collapses the cloud to a point.

Row 3 is the mirror image, and shows why you cannot solve this by handing Talker only text: "read" is one token with two pronunciations, and only the surrounding meaning tells you which. So each input covers the other's blind spot:

Hidden state h — continuous
Carries: tone, attitude, certainty, the unfinished plan, discourse context, and the meaning that disambiguates homographs. Blind to: exactly which word was chosen.
↓ fused inside Talker
Sampled token embedding — discrete
Carries: the exact lexical identity, hence the exact phoneme sequence, including irregular spellings. Blind to: everything about how it should be said.
The general pattern: continuous for style, discrete for identity. This shows up everywhere once you look. Diffusion models take a continuous conditioning vector plus discrete class labels. Speech models take speaker embeddings (continuous) plus phoneme IDs (discrete). The rule is: use continuous signals for the properties that vary smoothly and matter in aggregate, and discrete signals for the properties where being almost right is being wrong. Pronouncing a word almost right is being wrong.

The claim is now precise enough to test visually: two spaces, organized by different things, and a word that is unambiguous in one and ambiguous in the other.

Explore the two-input argument

Semantic space versus phonetic space — the ablation lab

Left: words positioned by meaning (a stylized view of Thinker's representation space). Right: the same words positioned by sound. Pick an input mode and press Speak — the ghost shows which words Talker could confuse. Illustrative layout; the mechanism it demonstrates is exactly the paper's stated argument.

qwen-tts-tokenizer: the vocabulary Talker writes in

Talker does not emit a waveform. It emits discrete codes from a purpose-built speech codec the paper calls qwen-tts-tokenizer, described as follows: it "efficiently represents key information of speech and can be decoded to speech streamingly through a causal audio decoder".

Two properties, both essential:

The report does not publish the codec's frame rate, codebook count, or vocabulary size. For calibration, the neighbouring systems of the era operate roughly in the range of 12.5–75 codes per second per codebook with 1–8 residual codebooks; we will treat the rate as a slider in Chapter 7 rather than pretend to a number.

The most consequential sentence in this section, easy to miss. "The generation of speech does not require word-level and timestamp-level alignment with the text. This significantly simplifies the requirements for training data and the inference process." Classical TTS pipelines need forced alignment — an external model marking which milliseconds correspond to which phoneme — and building that alignment for millions of hours is expensive, brittle, and language-specific. By making Talker learn a monotonic mapping from semantic representation to speech rather than a timed one, they delete the entire alignment stage. This is a data-pipeline victory dressed as an architecture note.

Which leaves one phrase from the report still undefined, and it is the phrase readers most often misunderstand.

What "dual-track" means

Talker is described as "a dual-track autoregressive Transformer Decoder architecture, motivated by Mini-Omni". And elsewhere: "After receiving the information, Talker starts to autoregressively generate audio tokens and text tokens."

So Talker maintains two parallel token tracks in one autoregressive process: a text track and an audio-code track. Mini-Omni's contribution (Xie & Wu, 2024) was exactly this idea — keeping a text stream alongside the audio stream so that the language-modelling signal keeps the audio generation coherent.

The intuition: generating hundreds of acoustic codes with nothing but acoustic history is a recipe for drift — the model wanders off, repeats, or trails into babble. Carrying a text token alongside gives every step an anchor in symbol space. It is the same insight as Moshi's "Inner Monologue", arrived at independently and used in a different architectural position.

Do not confuse "dual-track" with "full duplex". Both phrases describe two simultaneous streams, and they mean completely different things. Talker's dual track is two tracks of its own output (text and codes). Moshi's dual stream is the model's output and the user's input, simultaneously, which is what allows interruption and overlap. Qwen2.5-Omni is turn-based. Nothing in the report claims listening-while-speaking, and Chapter 9 counts that as a limitation.

All of which reduces, in code, to one small object with two inputs and two heads.

Code: the Talker step

python — one Talker step, from scratch
class Talker:
    """Dual-track AR decoder. Consumes Thinker's state + sampled token,
       emits (text_track, code_track) autoregressively."""

    def fuse(self, h_think, text_tok):
        # h_think: (B, 1, d_think)  continuous  -> tone, attitude, plan
        # text_tok: (B, 1)          discrete    -> exact lexical identity
        c = self.proj_state(h_think) + self.embed_text(text_tok)
        return c                          # (B, 1, d_talker)

    def step(self, c):
        # shares Thinker's KV history: cross-turn, cross-modal context
        s = self.decoder(self.cache, c)      # (B, 1, d_talker)
        code = sample(self.head_code(s))    # qwen-tts-tokenizer id
        txt  = sample(self.head_text(s))    # the anchoring text track
        self.cache.append(code, txt)
        return code, txt

# ABLATIONS you can reason about:
#   drop proj_state(h_think)  -> flat, context-free prosody (a TTS)
#   drop embed_text(text_tok) -> "cat" may come out as "dog"
#   drop shared cache         -> no memory of the video or the last turn

The three ablation comments are the chapter in three lines. Each input is load-bearing, and each failure mode is different in kind — which is the strongest possible evidence that the design is not over-engineered.

The argument for two inputs is made. What follows makes it precise — first as a probability model, then as the concrete constraints a speech codec has to satisfy, then as the prosody phenomenon that motivates the whole design.

Start with the probability model, because everything else in this chapter is a consequence of one term in it.

Talker as a conditional language model — the factorization

Strip away the words and Talker is a probability model. Writing down which probability makes the design decisions look like modelling decisions, which is what they are.

Let c1..N be the speech codes for a reply and let y1..M be the text tokens Thinker sampled. Talker models:

P(c1..N) = ∏n=1..N P( cn  |  c<n,   h≤m(n),   y≤m(n),   context )

Read the conditioning set left to right, because each element is a design decision you can now justify:

Conditioning termWhat it isWhat breaks without it
c<nThe codes already emittedNothing is autoregressive; the audio has no continuity at all
h≤m(n)Thinker's hidden states up to the text token being spokenProsody planned from the future disappears — you have a TTS
y≤m(n)The sampled text tokens themselvesLexical identity is ambiguous — "cat" may be spoken as "dog"
contextThinker's full history: the conversation, the video, the previous turnsVoice behaviour that depends on the situation disappears

The interesting object is m(n) — the map from "which code am I emitting" to "how far has the text got". Classical TTS fixes m by forced alignment: code n corresponds to a known millisecond, hence a known phoneme, hence a known word. Talker does not. All it is required to respect is that m is monotonic non-decreasing: as codes advance, the text position never goes backwards.

That is a strictly weaker constraint, and the paper says so in its own words: Talker "learns to establish a monotonic mapping from semantic representation to speech", and "the generation of speech does not require word-level and timestamp-level alignment with the text."

What monotonicity buys, stated as freedom. Under a fixed alignment, the model must say word 3 at millisecond 840, whatever it thinks. Under monotonicity, it must merely say word 3 after word 2. So it is free to linger on a word for emphasis, to compress a parenthetical, to place a hesitation — all of which are prosodic decisions, and all of which depend on the hidden states. The weaker constraint is not a concession to a lack of data. It is the room in which contextual delivery becomes expressible.
Inline check on the factorization. In the product above, which term makes the generation streaming rather than batch? The conditioning set contains only c<n, h and y up to m(n), and prior context — nothing from the future. That is what lets code n be emitted the moment its predecessors exist. If the factorization had conditioned on the whole text y1..M, the model would be a perfectly good non-streaming TTS and no amount of decoder engineering downstream could recover the latency.

Residual codebooks, and why "dual track" is not the same thing

One clarification that trips people up, because two different kinds of "multiple streams" live in this area.

Residual quantization is a codec technique. A single continuous vector is quantized by a first codebook; the error is quantized by a second; that error by a third, and so on. Each additional codebook refines the reconstruction. So one instant of audio is represented by several codes stacked in depth, not spread in time.

v  →  q1(v)  ;  r1 = v − q1(v)  →  q2(r1)  ;  r2 = r1 − q2(r1)  →  …
reconstruction ≈ q1 + q2 + q3 + …

More codebooks means higher bitrate and better fidelity. Truncating the stack after the first few gives you a lower bitrate for free — which is why codec-based systems can offer a bandwidth dial.

Dual-track is something else entirely: two parallel token types generated by the same autoregressive process — text and speech codes — where the text acts as a symbolic anchor for the acoustics.

Residual codebooksDual trackDual stream (Moshi)
What the streams containSuccessive refinements of the same audio instantThe model's text and the model's speech codesThe model's audio and the user's audio
AxisDepth (fidelity)Modality of the model's own outputSpeaker
PurposeBitrate versus qualityKeep long acoustic generations coherentListen and speak at once
Present in Qwen2.5-Omni?Very likely, unstatedYes — statedNo

The last row of the last column is the one to remember. Every time you see "dual" in this literature, ask which axis. Getting it wrong is how people come away believing Qwen2.5-Omni can be interrupted.

The factorization tells us Talker emits discrete codes, one after another, conditioned on the past. It does not tell us what those codes are. That is the codec's job, and although the paper withholds its specifications, the constraints it must satisfy are fully derivable from the streaming requirement.

Deriving them is worth doing, because it shows why a purpose-built codec was needed rather than an off-the-shelf one.

What a speech codec has to give you, quantitatively

The paper does not publish qwen-tts-tokenizer's specifications, but the constraints it must satisfy are derivable, and deriving them tells you why the component exists.

Start from the target: 24 kHz output audio, say. That is 24,000 numbers per second. An autoregressive model cannot generate 24,000 tokens per second of speech — at even 10 ms per forward pass you would need 240 seconds of compute per second of audio. So the codec must compress hard.

How hard? Work backwards from the compute budget:

ConstraintReasoningImplied code rate
Must run faster than real timeSpeech is generated as it is played; if generation is slower than playback the buffer drainsFewer codes/s than 1 / (per-step latency)
Per-step latency on a 7B-class decoderRoughly 5–20 ms on a modern accelerator≤ 50–200 steps/s
Must leave headroom for ThinkerThinker is also stepping, on the same deviceComfortably below that ceiling
Must preserve enough detail to sound humanBelow ~10 codes/s per codebook, prosody and identity start to go≥ ~12 codes/s

Contemporary systems land between 12.5 and 75 codes per second per codebook, usually with several residual codebooks stacked. That is the window qwen-tts-tokenizer must live in, and it is why the chapter after next treats the code rate as a slider rather than asserting a number.

Compression ratio, for scale: at 25 codes per second against 24,000 samples per second, one code carries 960 samples' worth of sound. Compare Chapter 2's input side, where one audio token carried 640 samples. Roughly the same order — the input and output bottlenecks are balanced, which is not an accident.

The property that matters more than the rate: causality. Many excellent codecs decode with bidirectional or global attention, which is fine when you have the whole utterance. The report specifies that qwen-tts-tokenizer "can be decoded to speech streamingly through a causal audio decoder". If it could not, the entire streaming apparatus downstream — the sliding-window DiT, the chunked BigVGAN — would be pointless, because the codec would already have forced you to wait. Causality is not one feature among many; it is the precondition for the rest of Chapter 6 to mean anything.

Two loose ends remain about Talker: how the two input signals are combined, and what "dual track" does and does not mean. Neither changes the argument, but both are places where readers routinely form wrong beliefs, so they are worth closing explicitly.

Take the fusion question first. The report is silent on it, and silence in a technical report is data: it usually means the choice was not load-bearing enough to defend. That turns out to be exactly right here.

Where the two signals could be fused, and why it does not much matter

The report says Talker "receives both high-level representations and embeddings of the text tokens sampled by Thinker" without saying how the two are combined. Three options are standard; it is instructive that the argument for needing both survives all three.

FusionShapeCharacter
Addition(1, d) + (1, d) → (1, d)Cheapest. Assumes the two live in compatible spaces after a projection — reasonable, since both come from the same model.
Concatenate then project(1, 2d) → (1, dtalker)Lets the projection weight the two sources differently per dimension. Slightly more parameters, strictly more expressive.
Cross-attentionTalker queries Thinker's states as keys/valuesMost flexible: Talker can attend to several past states rather than one. Also the most expensive per step.

Whichever is used, the load-bearing claim is unchanged: two different kinds of signal are required, because they carry complementary information. Semantic representation cannot pick phonemes; a token cannot pick a delivery. That argument is about information content, not about matrix shapes, which is why this lesson can teach it confidently while flagging the fusion operator as unstated.

A useful reading habit. When a report omits a detail, ask whether the argument depends on it. If yes, you have found a genuine gap and should say so. If no — as here — the omission is a design freedom rather than a hole, and the honest move is to name the options and move on. Confusing the two is how lessons end up either falsely confident or uselessly hedged.

The constraints are derived. Now the phenomenon they exist to serve — and it is worth feeling before it is analysed, because the argument only lands once you have heard the ambiguity in your own head.

The prosody problem, made concrete

"Prosody depends on the whole sentence" is easy to assert and easy to under-feel. Here is a sentence with two readings.

"I didn't say she took the money."

Stress any one of the seven words and the meaning changes completely: I didn't say it (someone else did); I didn't say it (I implied it); she took the money (not the jewellery). Seven readings, one string.

Now run a streaming voice generator over it. At the moment it must produce the sound for "I", the correct stress pattern depends on which of the seven meanings the speaker intends — and none of the disambiguating words have been generated yet.

A text-only TTS is stuck. It picks a default contour and hopes. A Thinker-Talker system is not stuck, because Thinker's hidden state at the "I" token already encodes which reading it is producing — the model decided that before it emitted a single word.

This is the strongest argument in the paper, and it is the one nobody measured. Chapter 9 will note that speech generation is evaluated only in a TTS framing: seed-tts-eval measures word error rate and speaker similarity given a text and a reference voice. Neither metric can see contextual prosody. So the mechanism that most justifies the architecture — that the voice knows where the sentence is going — is argued for, built for, and then not tested. That is a gap, and it is worth holding onto: it is exactly the kind of thing a follow-up paper is made of.

Two consequences of that phenomenon remain: what it means for the training data, and how you would test whether a system actually has the capability. The data question first.

Monotonic versus aligned — and what forced alignment costs

Chapter 5 already flagged that Talker learns a monotonic mapping and needs no timestamp alignment. Make the distinction sharp, because it is the difference between two eras of speech synthesis.

Aligned (classical TTS)Monotonic (this paper)
What the training data must containFor every audio file: which milliseconds correspond to which phonemeJust the audio and the text, in order
How that alignment is obtainedA forced aligner — typically an HMM or CTC model run over the corpusNot obtained at all
Failure modes it introducesAligner errors propagate silently into training targets; performance varies by language and accentNone — the stage does not exist
Cost at scaleCompute over the entire corpus, plus a per-language aligner to build and maintainZero
Constraint on the modelStrong: the model is told exactly when to say whatWeak: the model is told only the order

The weaker constraint is the better one here, and the reason is subtle. Timing is not a fact about the text; it is a choice about delivery. A forced alignment pins the model to one particular speaker's timing on one particular recording. Requiring only monotonicity lets the model choose its own pacing — which is precisely the freedom you need if the pacing is supposed to depend on the emotional context that Thinker's hidden states carry.

So the alignment-free design is not only cheaper. It is the design that makes contextual prosody possible. Removing a constraint removed a ceiling.

The mechanism is established and the data implications are drawn. One practical question closes the chapter: given a black box, how would you tell whether it has any of this?

Four tests that distinguish Talker from a TTS

If someone hands you a black box and claims it is a Thinker-Talker system rather than an LLM with a TTS glued on, here is how you would tell.

TestWhat a TTS doesWhat Talker should do
Ask a question whose answer is bad newsNeutral delivery — the words do not contain the registerSoftened, slower delivery from the first syllable
Show it something funny on camera, then have it replyUnchanged — it never saw the videoAmusement audible in the voice; the shared context includes the frames
Same sentence, two conversational contextsByte-identical audio both timesDifferent prosody — the hidden states differ even though the text does not
Interrupt mid-replyDepends entirely on the wrapperAlso depends on the wrapper — this one Qwen2.5-Omni does not claim (Ch 9)

Rows 1–3 are what the architecture buys. Row 4 is the honest boundary: the handoff makes the voice context-aware, not interruptible. Those are different problems, and Moshi solved the second one while Qwen2.5-Omni solved the first.

Inline check. Which of the three signals crossing the handoff would you remove first if you had to save bandwidth? The sampled token embedding is the smallest — but removing it costs pronunciation, and mispronouncing words is immediately, obviously broken. The hidden state is larger, and removing it costs prosody, which degrades gracefully into "sounds like a TTS". So the cheap-looking removal is the catastrophic one. Bandwidth is a bad axis on which to make this decision; failure severity is the right one.
Why does Talker need the sampled discrete text tokens in addition to Thinker's hidden representations?
The paper notes that speech generation "does not require word-level and timestamp-level alignment with the text". Why does that matter so much?

Chapter 6: Streaming Everything

Here is the observation that organizes this chapter: a pipeline streams only if every stage streams. One blocking stage anywhere and the whole thing becomes batch. If the audio encoder needs the full clip, it does not matter how clever the decoder is.

So Qwen2.5-Omni modifies every component. This chapter walks the stack from microphone to speaker and, at each stage, asks the same two questions: what does this stage need to see before it can emit, and how was that requirement made finite?

What "streamable" means, formally

Define, for any stage, its lookahead L: the amount of future input required before output for time t can be produced. Then:

stage is streamable  ⇔  L is finite and bounded
algorithmic delay of the pipeline = ∑ Lstage  (plus compute time)

Two consequences worth stating explicitly, because they are the whole design logic:

Stage 1 — the audio encoder: from full attention to 2-second blocks

The report: "the audio encoder is changed from full attention over the entire audio to performing attention in blocks of 2 seconds each."

Whisper's encoder, which this one is initialized from, does bidirectional full attention over a fixed 30-second window. Every frame sees every other frame. For transcription of a recorded file that is ideal. For live audio it is a wall: nothing can be encoded until 30 seconds exist, and every new sample invalidates the whole computation.

Block-wise attention partitions the time axis into consecutive blocks and confines attention to within a block. With 40 ms frames, a 2-second block holds

2.0 s / 0.040 s = 50 encoder frames per block

Cost accounting is instructive. Full attention over an N-frame clip costs O(N2). Block attention with block length B costs O((N/B) · B2) = O(N · B) — linear in the clip length. For a 60-second clip: N = 1,500 frames, so full attention touches 2,250,000 pairs while block attention touches 1,500 × 50 = 75,000. A 30× reduction, and it is the difference between a cost that explodes with call duration and one that does not.

Why local attention is not a compromise here. It would be, for a language model — a pronoun can refer twenty sentences back. It is not, for an acoustic encoder. Whether a 40 ms slice is a fricative or a vowel is settled by its immediate neighbourhood; two seconds is generous. And recall Chapter 1: the long-range job was deliberately handed to the LLM, whose attention is global. The encoder is not losing context; it is declining to duplicate work that happens better one level up.

Stage 2 — the vision encoder

The report is briefer here: "The vision encoder utilizes flash attention for efficient training and inference with a simple MLP layer that merges adjacent 2×2 tokens into a single token. The patch size is set to 14, which allows images of different resolutions to be packed into a sequence."

Three mechanisms, each doing a different job:

MechanismWhat it buysWhich latency term it attacks
Flash attentionMemory-efficient exact attention — no O(N2) materialized matrixTerm 4 (raw compute) — lets you afford the frames you sampled
2×2 MLP merge4× fewer tokens entering the LLMTerms 1 and 4 — both encoding and prefill shrink
Patch 14 + resolution packingVariable-resolution images share one batched sequenceThroughput; also lets you drop resolution when the clock is tight

Video streams naturally: a frame is a self-contained unit, so frames can be encoded as they arrive. The relevant knob is not lookahead but rate — and Chapter 2's dynamic frame rate is what turns that knob.

Stage 3 — chunked prefill in the LLM

"Chunked-prefills is a mechanism widely used in modern inference framework. To support it in modalities interation, we modified the audio and visual encoders to support block-wise attention along the temporal dimension."

Prefill is the forward pass over the prompt before any token is generated. It is one big parallel matrix operation, and for long multimodal prompts it dominates time-to-first-token. Chunked prefill splits it: process 512 tokens, then the next 512, and so on, interleaving with decoding work from other requests to keep the GPU saturated.

Here is the dependency that this chapter exists to expose. Chunked prefill requires that a prefix of the input be meaningful on its own. If the encoders used full attention over the entire clip, then no prefix of the token sequence is final — every token's value depends on audio that has not arrived. Chunking the prefill would be computing on values that are about to change.

Block-wise encoders
Make each 2-second block's tokens final as soon as that block is complete.
↓ enables
Time-interleaving in 2 s chunks
Makes the sequence order agree with block availability — V then A, chunk by chunk.
↓ enables
Chunked prefill
The LLM can consume the prompt incrementally, overlapping with the audio still arriving.
↓ enables
First text token, early
Which starts the Thinker→Talker handoff, which starts the codes, which starts the audio.

That chain is the answer to "why 2 seconds, in both places?" — the encoder block size and the interleave chunk size are the same number because they are the same boundary. A different pair of numbers would create a partial-block state that neither mechanism knows how to represent.

Stage 4 — the sliding-window DiT, the paper's prettiest piece of engineering

Talker emits codes. Something must turn codes into a waveform, and that something is a two-step decoder: a flow-matching DiT (Diffusion Transformer) that produces a mel-spectrogram from the codes, then a modified BigVGAN that turns mel into samples.

Flow matching, in one paragraph, because you need the shape of it: instead of learning to denoise across many steps, you learn a velocity field that transports a simple noise distribution to the data distribution along straight-ish paths. At inference you integrate that field over a small number of steps. It is faster than classical diffusion and produces high-quality mel, which is why it dominated TTS around 2024–2025 (F5-TTS, CosyVoice 2, E2 TTS all use it).

The problem: a DiT is a transformer, and its default attention is global over the whole mel sequence. That is L = ∞. So the paper restricts it.

"We propose a sliding window block attention mechanism that restricts the current token's access to a limited context… we group adjacent codes into blocks and use these for our attention mask. We limit the DiT's receptive field to 4 blocks, including a lookback of 2 blocks and a lookahead of 1 block."

Count the four: 2 lookback + 1 current + 1 lookahead = 4. The current block is included in the receptive field, which is how the arithmetic closes.

BlockRoleWhy it is needed
−2LookbackProsodic continuity — pitch contour and energy must continue smoothly from what was already played
−1LookbackImmediate coarticulation — the tail of the previous phoneme shapes the onset of this one
0CurrentThe block actually being decoded
+1LookaheadAnticipatory coarticulation — the mouth prepares for the next sound before finishing this one. Without it, block boundaries click.
Asymmetry is the design. Two blocks of past, one of future. Lookback is nearly free: those blocks already exist, sitting in the cache, and using them costs only compute. Lookahead is expensive: every block of it is a block of waiting, added directly to the delay before the first sound. So you buy the cheap thing generously and the expensive thing minimally — one block, the smallest amount that still lets the model anticipate. That asymmetry is not aesthetic; it is the shape of the cost function.

Why any lookahead at all? Because speech production is anticipatory. Say "true" and "tea" out loud and feel where your lips are during the /t/: rounded for "true", spread for "tea". The consonant is physically shaped by the vowel that has not happened yet. A decoder with zero lookahead must guess, and it will guess wrong at exactly the block boundaries — producing the audible discontinuities that plague naive streaming vocoders.

One stage remains in the chain from microphone to speaker.

Stage 5 — BigVGAN, chunked

"a modified BigVGAN to reconstruct the generated mel-spectrogram back into the waveform… We also use this chunk-by-chunk method for BigVGAN's fixed receptive field to facilitate streaming waveform generation."

BigVGAN is a convolutional GAN vocoder. Convolutions have a fixed receptive field determined by kernel sizes and dilations — a known, finite number of mel frames on each side. That is already the good case: no attention, so no unbounded dependency. The only work needed is bookkeeping — feed it overlapping chunks with enough context on each side that the output is identical to what the full-sequence pass would have produced, then discard the overlap.

This is the same trick used in overlap-add convolution and in streaming CNN inference generally. It costs a little redundant compute at the boundaries and nothing in quality.

Four mechanisms, four masks. Seeing them side by side is the fastest way to internalize which ones can stream and why.

See the masks

Attention masks — what each mechanism actually allows

Rows are queries, columns are keys; a filled cell means "may attend". Switch mechanism and watch the shape change. The readout computes the receptive field, the lookahead in blocks and in real time, and whether the mechanism can stream at all.

Set lookahead to 0 and the mask becomes strictly block-causal — zero added delay, and the boundary artifacts we just discussed. Set it to 3 and the mask gets a comfortable future window at three times the delay. The paper's setting, 2 / 1, is the point where the curve of "quality per millisecond of delay" is steepest.

Finally, the masks as code — three functions, one of which is not streamable, and the arithmetic that turns a mask into milliseconds.

Code: the three masks, and why only two of them stream

python — building the masks from scratch
import numpy as np

def full_mask(n):
    return np.ones((n, n), bool)                  # lookahead = infinity

def block_mask(n, B):
    # attention confined WITHIN each block of B frames
    b = np.arange(n) // B
    return b[:, None] == b[None, :]              # lookahead <= B-1 frames

def sliding_block_mask(n, B, lookback, lookahead):
    b  = np.arange(n) // B
    db = b[None, :] - b[:, None]                  # key block - query block
    return (db >= -lookback) & (db <= lookahead)

n, B = 24, 4
m = sliding_block_mask(n, B, lookback=2, lookahead=1)   # the paper's DiT
print(m[12].sum())        # 16 = 4 blocks x 4 frames -> receptive field
print(int(np.argmax(m[12][::-1] == False)))   # future frames visible

# delay accounting, in real time:
CODE_HZ = 25                       # codes per second (illustrative)
print((B * 1) / CODE_HZ)          # 0.16 s of lookahead delay at B=4, la=1

The last two lines are the point. Lookahead is not an abstract hyperparameter; multiply blocks by block size, divide by the code rate, and you get milliseconds you can feel. Chapter 7 assembles all such terms into one ledger.

Checklist for streaming any pipeline — steal this. (1) For every stage, write down its lookahead in real time, not tokens. (2) Any stage with unbounded lookahead must be restricted or moved off the critical path. (3) Sum the finite ones — that is your floor, before compute. (4) Align the block boundaries of adjacent stages, or you will pay for the mismatch twice. (5) Spend lookahead only where the future genuinely determines the present: coarticulation yes, vocoding no.

Two more things to settle before the cost arithmetic: what the block approximation genuinely gives up, and how to place any streaming system on a common set of axes. Both are the kind of framing that outlives the specific paper.

What block-wise attention actually loses

Chapter 1 argued that perception is local, so confining the encoder to 2-second blocks costs nothing. That argument is mostly right, and being precise about the "mostly" is worth a section.

Here are three real acoustic dependencies that reach further than two seconds:

DependencyRangeDoes the block boundary hurt?
Speaker identity — is this the same voice as before?Whole conversationNot really. Voice timbre is estimable from a fraction of a second; each block independently recovers it.
Room acoustics / channel — the reverberation signatureWhole recordingNot really. Also estimable locally, and stationary, so every block sees the same evidence.
Long-range prosodic structure — a question intonation spanning a long clauseSecondsSomewhat. A contour that crosses the boundary is only seen in halves by the encoder.

The first two rows are the reason the approximation is safe: the properties that genuinely span a whole recording are also stationary, so any two-second window contains enough evidence to estimate them. Locality hurts when a property is both long-range and non-stationary, and most acoustic properties are not both.

The third row is a real, if modest, loss — and it is exactly the case the architecture recovers at the next level up. The LLM sees all the blocks in one sequence with full attention, so a prosodic contour spanning a boundary is still recoverable from the token sequence; it just is not resolved inside the encoder. That is the division of labour from Chapter 1, doing its job.

The honest framing. Block attention is an approximation, not a free lunch, and the paper does not ablate it. What it does is choose a block size long enough that the approximation is defensible for the properties that matter, and place the exact mechanism (full attention) one level up where the long-range work belongs. "Approximate locally, be exact globally" is a pattern worth carrying to other systems.
Inline check. The audio encoder was changed from full to block attention, and the DiT from global to sliding-window. Why did the DiT get a sliding window while the encoder got hard block boundaries? Because the DiT is synthesizing a continuous waveform, where a discontinuity at a boundary is immediately audible; overlapping receptive fields hide the seam. The encoder is producing features for an LLM that has full attention over all of them anyway, so a hard boundary costs much less. The masks differ because the consumers differ.

A taxonomy of streaming, for placing any system

Since "streaming" gets used for four different things, here is the vocabulary, with where Qwen2.5-Omni sits on each axis.

AxisOptionsQwen2.5-Omni
Input streamingBatch (needs the whole clip) / chunked / sample-by-sampleChunked, 2-second granularity
Output streamingWait for the full reply / sentence-by-sentence / block-by-blockBlock-by-block, at the DiT block granularity
IncrementalityCan emitted output be revised? Or is it final?Final — audio, once played, is played
DuplexityHalf duplex (turns) / full duplex (simultaneous)Half duplex

The third row is the one people forget. Some streaming ASR systems emit hypotheses and revise them; you have seen text change on screen as you keep speaking. Audio cannot do that — once a sample is played, it is committed. That irreversibility is why lookahead exists at all: it is the only way to be a little bit right about the future before committing.

Enough framing. The arithmetic is what makes the block-attention argument concrete, and it is short.

The cost table, worked out

"O(N·B) instead of O(N2)" is the kind of statement that slides past. Put numbers on it and it becomes a reason.

Clip lengthFrames N (at 25/s)Full attention pairs (N2)Block attention pairs (N × 50)Ratio
2 s502,5002,5001× — identical, by construction
10 s25062,50012,500
30 s750562,50037,50015×
60 s1,5002,250,00075,00030×
5 min7,50056,250,000375,000150×

The first row is the important one: at exactly the block size, the two schemes are identical. Block attention costs nothing until the input is longer than a block — and then the saving grows without bound. That is the signature of a good approximation: free where it does not matter, decisive where it does.

The second thing to read off the table is that the block-attention column grows linearly. A ten-minute call costs twice a five-minute call, not four times. For a live system that is the difference between a cost model you can serve and one you cannot.

Three things remain in the streaming story: the asymmetry of the DiT's window, what flow matching is doing inside it, and how the vocoder gets chunked. The asymmetry first, because it is the most portable idea here.

Why lookback is nearly free and lookahead never is

The asymmetry deserves an argument rather than an assertion, because it is the single most reusable idea in this chapter.

Consider decoding block n. The lookback blocks — n−2 and n−1 — were decoded already. Their codes exist; their keys and values are sitting in a cache. Attending to them costs some arithmetic and no waiting whatsoever. The information was free; only the FLOPs are charged.

Now the lookahead block n+1. Its codes do not exist yet. Talker has to be stepped further before block n can be decoded at all. Every code of lookahead is a code the system waits for, and that wait lands directly on time-to-first-audio:

delay contributed by lookahead = lookahead × B / code_rate
delay contributed by lookback = 0

So the cost functions have completely different shapes: lookback is priced in FLOPs, lookahead in milliseconds. A rational designer therefore buys lookback until compute complains and lookahead only until the artifacts stop. Two blocks and one block is what that looks like.

Inline check. Suppose you had a magically fast accelerator, so compute were free. Would you increase the lookahead? No — not by one block. Lookahead delay is structural: it is waiting for codes that have not been generated, and no amount of hardware creates them sooner. This is the cleanest possible illustration of the chapter's thesis that latency lives in the boundaries, not the layers.

The asymmetry explains the shape of the window. What it does not explain is what the DiT is doing inside it, and that turns out to matter for why a transformer is affordable here at all.

Flow matching, in five lines

The DiT is a flow-matching model, and we have used the term without unpacking it. Here is the whole idea, because it explains why the paper could afford a transformer in the vocoder path at all.

The goal is to turn a sample from a simple distribution (Gaussian noise) into a sample from a complicated one (a mel-spectrogram consistent with these codes). Classical diffusion does this with many small denoising steps along a curved, learned path. Flow matching instead learns a velocity field and defines an almost-straight path:

x(t) = (1 − t) · x0 + t · x1,  t from 0 to 1
where x0 is noise and x1 is the target mel

Train vθ(x, t, codes) to predict the velocity   dx/dt = x1 − x0
Sample by integrating:   x ← x + Δt · vθ(x, t, codes)

Work one Euler step by hand on a scalar, so the loop is concrete. Say x0 = 0.0 (noise), and the network predicts velocity 2.4 at t = 0 with 8 integration steps, so Δt = 0.125:

x ← 0.0 + 0.125 × 2.4 = 0.300  (t = 0.125)
next step, network predicts 2.2:   x ← 0.300 + 0.125 × 2.2 = 0.575  (t = 0.250)
… eight steps later, x has arrived at the target value.

Eight forward passes, not a thousand. That is the entire practical appeal: because the path is nearly straight, few steps suffice, and a transformer becomes affordable inside a real-time audio path. Every serious 2024–2025 TTS system in the comparison table — F5-TTS, E2 TTS, CosyVoice 2 — made the same bet.

Why the DiT needs a mask at all, when the codes already exist. A reasonable objection: Talker has produced the codes, so why does the decoder need lookahead? Because the DiT is not doing a per-code lookup — it is synthesizing a continuous signal, and the mel frames at a block boundary depend on codes on both sides. Restricting the mask restricts how far that dependence reaches, which is what makes the block decodable before the rest of the codes are generated. The mask is not about attention cost here; it is about when the block becomes computable.

One stage left in the chain, and it is the easy one — easy for a reason that is itself the lesson of this chapter.

BigVGAN, chunked — the arithmetic

The vocoder is the easy case and it is worth seeing why, because it shows what "streamable" looks like when a component was already well behaved.

BigVGAN is a stack of transposed convolutions and residual blocks with dilations. Its receptive field is fixed: a given output sample depends on a bounded window of mel frames, say R frames on each side. To produce output for a chunk of C mel frames, you feed the chunk plus R frames of context on each side and keep only the middle:

feed:   C + 2R mel frames
keep:   the C frames' worth of samples in the middle
redundant compute:   2R / (C + 2R)

With, say, R = 16 and C = 64 that is 32 / 96 = 33% redundant compute — and output that is bit-identical to a full-sequence pass. Double the chunk to C = 128 and the waste falls to 20%. This is overlap-save convolution, a technique older than any of the models here.

The reason this component needed only bookkeeping and not an architectural change: convolutions have finite receptive fields by construction. Attention does not. That single distinction explains why the audio encoder and the DiT both needed new masks while the vocoder needed only a loop.

Inline check. Rank the four streaming changes by how much architectural surgery they required. BigVGAN: none, just chunked invocation. Vision encoder: none for streaming — frames are already independent units. Audio encoder: a real change, full attention to block attention, which alters what the model can represent. DiT: also a real change, a bespoke asymmetric mask. The pattern: components whose dependency structure was already local needed nothing; components built on global attention needed to have that globality taken away.
The DiT's receptive field is limited to 4 blocks. How are those 4 blocks composed?
Why does chunked prefill require the block-wise encoder change?

Chapter 7: SHOWCASE — The Handoff and the TTFA Ledger

Everything so far has been a component. This chapter assembles them and puts a stopwatch on the result.

The quantity we are measuring is TTFAtime to first audio. Not time to complete answer; time until the first sample of the reply reaches the speaker. It is the number a listener actually experiences as "responsiveness", and it is what the paper calls initial packet latency.

Honesty note, up front. The report gives no wall-clock latency measurements. What it gives is a structure: four named contributors (Chapter 0) and the mechanisms that bound each one. This chapter builds the ledger from that structure, with per-stage costs you control. The terms and their dependencies are the paper's; the milliseconds are yours to set. That is more useful than a single published number anyway, because the whole point is that TTFA is a function of knobs.

Start where every latency analysis should start: with a concrete request, and a list of everything that has to happen before the first sound.

Trace one turn, end to end

The user speaks a 4-second question while pointing at something on camera. Follow the data.

#StageInput → output (shapes)Blocks onAdds to TTFA?
1Mic & resamplewaveform → (64000,) at 16 kHzReal time — 4 s of speech takes 4 sNo — this is the user's time, not the model's
2Mel front end(64000,) → (400, 128)10 ms hop; effectively instantaneousNegligible
3Audio encoder(400, 128) → (100, d) — one token / 40 msIts 2-second block boundaryYes — term 1
4Vision encoder(F, 3, H, W) → (F × g2, d), g = px/14/2Frame arrivalYes — term 1
5Interleave + TMRoPEtoken list → (T, d) plus (T, 3) position idsChunk boundary (2 s)Yes — term 1
6Thinker prefill(T, d) → KV cacheChunked; overlaps with 3–5Yes — term 1/4
7Thinker decode stepKV → h: (1, d), logits: (1, 151643)One forward passYes — term 2
8Handoffh (1, d) + embed(tok) (1, d) → (1, dtalker)Nothing — per stepNegligible
9Talker decode(1, dtalker) → codes (nc,)Enough codes for one DiT blockYes — term 2
10DiT (flow matching)codes (B(1+la),) → mel (B × r, 128)Current block + 1 lookahead blockYes — term 3
11BigVGANmel chunk → waveform samplesIts fixed conv receptive fieldYes — term 3

Two structural facts jump out of that table.

First, stages 3–6 overlap. While block 2 of the audio is being encoded, block 1 is already being prefilled. That is what chunked prefill buys: those costs are not additive, they are pipelined. Only the last chunk's encode-and-prefill sits on the critical path.

Second, the critical path is short. After the user stops speaking, what remains is: encode the final partial block, prefill it, run Thinker once, run Talker enough times to fill one block plus lookahead, run the DiT once, run BigVGAN once. Everything else already happened while the user was still talking.

With the stages enumerated and the overlaps identified, the ledger can be written down precisely.

The ledger, written out

Let us define the terms precisely, so the sim below is not a black box.

SymbolMeaningDepends on
tencEncode the final (partial) audio blockEncoder block size; how much of the block is left when speech ends
tprePrefill the final chunk of tokensChunk token count, model size
tthinkOne Thinker forward pass to the first text tokenModel size, KV length
ttalkTalker steps to produce B(1 + la) codesBlock size B, lookahead la, per-step cost
tditOne DiT block decode (flow-matching integration steps)Block size, number of ODE steps
tvocOne BigVGAN chunkChunk size, receptive-field padding
TTFASum of the aboveEvery knob at once

And the two structural terms that dominate when the knobs are set badly:

algorithmic delay from the DiT = (1 + lookahead) × B / code_rate
algorithmic delay from encoder blocking ≤ encoder block size (2 s worst case)

Work an example by hand. Suppose the codec runs at 25 codes per second, block size B = 8 codes, lookahead = 1 block. Then before the DiT can decode block 0 it needs blocks 0 and 1, i.e. 16 codes:

16 codes / 25 codes per second = 0.64 s of codes must exist first.

Now halve the block to B = 4: it needs 8 codes = 0.32 s. Halve again to B = 2: 4 codes = 0.16 s. The delay is linear in block size, which is why block size is the single most powerful knob in the whole ledger — and why the DiT's quality at small block sizes is the thing that ultimately bounds how responsive the system can be.

The trade you cannot escape. Smaller blocks mean less waiting and worse continuity — the DiT sees less context per decode, and boundary artifacts multiply. Bigger blocks mean cleaner audio and a longer silence before it starts. The lookback of 2 blocks is what makes small blocks survivable: even at B = 2, the DiT still sees 4 blocks of context (2 back, current, 1 ahead) — it just re-derives them more often. The paper is buying the ability to use small blocks by making the receptive field count blocks rather than frames.

Everything is now in place to build the instrument. The sim below is this lesson's showcase: the full pipeline with live shapes, and the ledger those shapes determine.

The showcase

Thinker → Talker → sound: live shapes and a TTFA ledger you control

The top half is the pipeline with live tensor-shape badges — change any slider and the shapes recompute. The bottom half is the TTFA ledger: a stacked bar of every term, with the streaming path against the non-streaming baseline (wait for the whole reply, then synthesize). Press Play to watch a packet travel. Per-stage compute costs are illustrative defaults; the structure and the dependencies are the paper's.

Now use it. The five experiments below are ordered so that each one isolates a different term of the ledger, and together they cover every claim the streaming design makes.

Five experiments to run in the sim

  1. Turn streaming off. The ledger collapses into one enormous bar: you must generate the whole reply, then synthesize the whole waveform. TTFA becomes proportional to reply length — a 200-token answer is a multi-second silence. This is the baseline every streaming decision is measured against.
  2. Set DiT lookahead to 0. Watch that term vanish from the bar. Then remember Chapter 6: this is the setting that produces audible clicks at block boundaries. Free latency, paid for in quality.
  3. Push the encoder block from 2 s to 8 s. The first term grows. Beyond about 2 seconds, blocking on the encoder starts to dominate everything the decoder side achieved — which is precisely why the paper chose 2.
  4. Raise the codec rate. At a fixed block size in codes, a faster codec means each block covers less time, so the lookahead delay shrinks — but the Talker must run more steps per second of speech, so its compute term grows. The two effects fight. Find where the total is minimized.
  5. Raise video resolution and frame rate together. Watch the visual token count explode and the prefill term with it. This is the moment the dynamic frame rate stops being a nicety and becomes the mechanism that keeps the system usable.

The ledger is built and the shapes are derived. Two things remain: what each knob costs in quality, and the honest boundary of what the sim models at all.

What each knob costs in quality, not just milliseconds

The ledger prices everything in time. Every knob has a second price, and a design that ignores it produces a fast system nobody wants to listen to.

KnobTurn it down and you save……and you payHow the damage shows up
Encoder block sizeUp to the block size, onceLess acoustic context per attention windowSubtle: slightly worse recognition on hard audio
DiT block size B(1 + la) × B / rate, every blockFewer codes decoded togetherAudible: more block boundaries per second, so more places to click
DiT lookaheadla × B / rateNo anticipatory context at all at la = 0Audible: discontinuities exactly at boundaries
Codec rateNothing directly — it is a tradeLower rate = coarser acoustic detailAudible: thinner, less natural voice
Video resolutionPrefill, quadratically in pixelsFewer patches per frameVisible: small text and fine detail lost — the video-OCR problem the authors name
Video frame ratePrefill, linearlyCoarser temporal samplingVisible: fast motion and brief events missed

Two of these rows interact in a way worth noticing. Lowering the codec rate makes each code cover more time, which shrinks the lookahead delay at fixed B — but it also coarsens the audio. So the codec rate is not a latency knob at all; it is a quality knob whose latency effect is a side effect. Reading it as a latency knob leads you to a system that is fast and sounds bad.

The rule for tuning any of this. Turn the knobs whose quality cost is subtle before the knobs whose cost is audible. Encoder block size first (subtle). Then video frame rate, if the task tolerates it (visible, but often irrelevant — a talking-head video does not need 5 fps). DiT block size and lookahead last, because listeners notice discontinuities immediately and forgive almost nothing about them.

Where the shapes come from

The shape badges are not decoration; every one is computed from a rule you have already derived. Collected here so you can verify the sim:

BadgeFormulaDerived in
Mel array(seconds × 100, 128)Ch 2 — 10 ms hop, 128 channels
Audio tokensseconds × 25Ch 2 — 40 ms per frame
Visual tokens per frame(px / 14 / 2)2Ch 2 — patch 14 then 2×2 merge
Total visual tokensframes × (px/14/2)2Ch 2
Prompt length Ttext + visual + audio tokensCh 4 — the interleaved sequence
Position ids(T, 3) — the (t, h, w) triplesCh 3
Thinker logits(1, 151643)Ch 2 — Qwen tokenizer vocabulary
Codes before first decodeB × (1 + lookahead)Ch 6 — sliding window

What the sim is not modelling

Honesty about the instrument, so you do not over-trust it.

What the sim does model correctly is the part the paper is actually about: which terms exist, what each one depends on, and how the structural waits dominate. That is the reasoning the architecture encodes, and it is reasoning that survives all four caveats above.

Three lessons close the chapter, and none of them is specific to speech.

Reading the ledger like an engineer

Three lessons that generalize past this paper.

Latency lives in the boundaries, not the layers. Almost every term above is a waiting term — waiting for a block, a chunk, a lookahead. Only two terms (prefill and the forward passes) are genuinely compute. When people optimize a slow voice pipeline they usually reach for a smaller model; the ledger says they should first look at what everything is waiting for.

Pipelining converts sums into maxima. The reason overlapping stages 3–6 matters so much is arithmetic: serial stages add, pipelined stages take the maximum. A pipeline is only as slow as its slowest stage plus the drain, not the sum of all stages. Every "block-wise" change in this paper exists to make a stage pipelineable.

Every knob is a quality knob too. There is no free latency in this system. Encoder block size trades against acoustic context. DiT block size trades against continuity. Frame rate trades against visual detail. The engineering skill is knowing which trade is cheapest at the current operating point — which is exactly what the sim lets you feel.

The one-sentence summary of the entire architecture. Qwen2.5-Omni is a system in which every stage has been given a finite lookahead, every pair of adjacent stages has been given a shared block boundary, and every representation that crosses a stage boundary has been kept continuous where it can be — so that the first sound of the reply can leave the machine while the machine is still deciding what to say.

Before the ledger, one visualization that makes the entire streaming argument obvious in a way the table cannot: put the stages on wall-clock time and see how much of the work happens while the user is still talking.

The turn as a Gantt chart

The table above lists stages. What it does not show is when each one runs, and that is where the whole design lives. Lay the four-second utterance on a timeline and mark what the machine is doing during it.

Wall clockUserEncodersThinkerTalker + decoder
0.0–2.0 sspeakingbuffering block 1idleidle
2.0 sspeakingblock 1 complete → encodeidleidle
2.0–4.0 sspeakingbuffering block 2prefilling chunk 1idle
4.0 sstopsencode block 2 (tail)waiting on chunk 2idle
4.0 s + tencdoneprefill chunk 2idle
+ tprefirst text tokenhandoff arrives
+ tthinkcontinues generatinggenerating codes
+ wait for B(1+la) codesDiT block 0 → BigVGAN
TTFAfirst sample leaves the machine

Read the third column of rows 3 and 4. While the user is still talking, chunk 1 is already prefilled. Its 2 seconds of encoding and its prefill cost are paid inside the user's own speaking time and never appear in TTFA at all.

Only two things remain after the user stops: the tail block (a partial one, so cheaper than a full block) and its prefill. Everything else on the critical path is generation, and generation could not have started earlier under any architecture — the model has to hear the question before it can answer it.

This is what "pipelining converts sums into maxima" means concretely. Naively, TTFA would be encode(4 s of audio) + prefill(2,156 tokens) + think + talk + decode. Pipelined, it is encode(the tail) + prefill(the last chunk) + think + talk + decode, because the earlier chunks were absorbed into time that was passing anyway. The saving is not a constant factor — it grows with the length of the user's utterance. A thirty-second question costs the same TTFA as a four-second one, which is a strange and wonderful property to have.

One consequence worth drawing out: a longer question does not mean a slower answer, up to the context limit. That is the opposite of the naive intuition, and it is only true because every input-side stage was made incremental. In a batch pipeline, TTFA scales with the length of the input; here it scales with the length of the last chunk, which is bounded by the chunk size.

Two settings, worked by hand

Do the ledger arithmetic yourself once, so the sim stops being a black box. Assume a codec at 25 codes per second and these illustrative per-stage compute costs (the shapes are the paper's; the milliseconds are ours):

StageCost model
Encode the trailing partial audio block125 ms per second of encoder block size
Chunked prefill of the final chunk0.055 ms per prompt token
One Thinker forward pass60 ms
Wait for codesB × (1 + lookahead) / 25 seconds — structural, not compute
Talker compute1.6 ms per code
One DiT block decode25 ms + 1.4 ms per code in the block
One BigVGAN chunk12 ms

Setting A — the paper-shaped defaults. Encoder block 2.0 s, DiT block B = 8 codes, lookahead 1, prompt T = 2,156 tokens.

TermArithmeticms
encode tail block2.0 × 125250
chunked prefill2156 × 0.055119
Thinker step60
wait for codes8 × (1 + 1) = 16 codes; 16 / 25 = 0.64 s640
Talker compute16 × 1.626
DiT block25 + 8 × 1.436
BigVGAN chunk12
TTFA250+119+60+640+26+36+121,143

Setting B — tuned for responsiveness. Encoder block 1.0 s, B = 4, lookahead 1, everything else identical.

TermArithmeticms
encode tail block1.0 × 125125
chunked prefillunchanged119
Thinker step60
wait for codes4 × 2 = 8 codes; 8 / 25 = 0.32 s320
Talker compute8 × 1.613
DiT block25 + 4 × 1.431
BigVGAN chunk12
TTFA125+119+60+320+13+31+12680

A 40% reduction, and look at where it came from: 320 ms from halving the wait for codes, 125 ms from halving the encoder block. Both are waiting terms. Not one millisecond came from making anything compute faster.

Amdahl's law, for latency. In setting A, 640 of 1,143 ms — 56% — is a single structural wait. Halve the model's FLOPs and you improve the compute terms (60 + 26 + 36 + 12 = 134 ms) by at most 67 ms: a 6% improvement. Halve the DiT block instead and you save 320 ms: 28%. When a system is dominated by waiting, optimizing compute is optimizing the wrong thing. This is the single most transferable lesson in the chapter, and it applies far beyond speech.

Two numbers is enough to see the shape. What follows is the honest widening: what a deployed system pays that this ledger does not count.

What a real deployment adds that this ledger does not show

Be honest about the gap between a ledger and a product. Everything above is what happens inside the model. A deployed voice agent also pays:

TermTypical scaleWho owns it
Microphone capture and buffering10–40 msThe client device
Network transport to the server20–150 ms round trip, worse on mobileThe internet
Jitter buffer20–100 msThe transport layer, protecting against packet loss
Endpointing / turn detection200–800 msThe agent framework — deciding you have finished speaking
Queueing on a shared server0 to secondsYour scheduler, under load
Playback buffer on the client20–80 msThe client device

The endpointing row often dwarfs everything the model does. A system that waits 700 ms of silence before deciding your turn ended has spent more time on that one heuristic than on the entire forward pass. This is why "our model is fast" and "the assistant feels fast" are different claims, and why turn-taking is its own research area.

It also reframes the paper's contribution correctly. Qwen2.5-Omni removes the model's contribution to latency as a bottleneck. It does not, and does not claim to, solve conversational responsiveness end to end.

Which brings us to the number the paper never gives. If you wanted to produce it honestly, here is what that would take.

A checklist for measuring TTFA yourself

Since the paper gives none, here is how you would produce the number it is missing.

  1. Define the start event precisely. Last sample of user audio received? Or the endpointer's decision? These differ by hundreds of milliseconds and papers quietly choose whichever flatters them. State it.
  2. Define the end event precisely. First audio sample produced, or first sample played? The playback buffer sits between them.
  3. Report the distribution, not the mean. Latency distributions are heavy-tailed. The p95 is what users perceive as "this thing is unreliable"; the mean hides it.
  4. Sweep the prompt length. Prefill scales with T, and T is dominated by video. A number measured on a text-only prompt tells you almost nothing about the video case.
  5. Report the knobs. Block sizes, lookahead, code rate, frame rate, resolution. Without them the number is unreproducible — as this chapter's two settings demonstrate, the same model can be 680 ms or 1,143 ms depending only on configuration.
  6. Separate structural from compute. Report the two sums separately. The first tells you about the design; the second tells you about the hardware. Conflating them makes both uninterpretable.
Inline check. Your voice agent has a 1,100 ms TTFA and your manager asks for 600 ms. From the ledger, name your first three moves. (1) Halve the DiT block — 320 ms, costs some continuity. (2) Halve the encoder block — 125 ms, costs some acoustic context. (3) Audit endpointing — possibly hundreds of milliseconds, costs nothing but engineering. Notice that "use a smaller model" is not in the top three, and that the third item is not even in the model.
The codec runs at 25 codes/s, the DiT block is 10 codes, and lookahead is 1 block. How much speech worth of codes must exist before the first mel block can be decoded?
Why do the audio-encoding and prefill costs largely not appear on the critical path for TTFA?

Chapter 8: The Curriculum

An architecture is a hypothesis. Training is where you find out. This chapter covers both halves of the regime: three pre-training stages for the whole model, and a separate three-stage path for Talker — which is where the most interesting technique in the paper hides.

Pre-training, stage by stage

The report: "Qwen2.5-Omni consists of three training stages. In the first stage, we lock the LLM parameters and focus exclusively on training the vision encoder and audio encoder, utilizing a vast corpus of audio-text and image-text pairs to enhance semantic understanding within the LLM. In the second stage, we unfreeze all parameters and train with a wider range of multimodal data for more comprehensive learning. In the final stage, we use data with a sequence length of 32k to enhance the model's ability to understand complex long-sequence data."

Stage 1Stage 2Stage 3
LLM🔒 Frozen🔓 Trained🔓 Trained
Vision encoder🔓 Trained (adapter first)🔓 Trained🔓 Trained
Audio encoder🔓 Trained (adapter first)🔓 Trained🔓 Trained
DataAudio-text and image-text pairs+800B image/video tokens, +300B audio tokens, +100B video-with-audio tokens, plus pure textLong audio and long video; text/audio/image/video extended to 32,768 tokens
Max sequence length8,1928,19232,768
GoalTeach the encoders to speak the LLM's languageDeepen cross-modal interaction; multi-task competenceLong-context understanding

Why freeze the LLM first?

This ordering is standard and the reason is worth stating properly, because it explains what an "adapter" is for.

At initialization, the audio encoder (Whisper-large-v3) produces vectors in Whisper's representation space. The LLM (Qwen2.5) expects vectors in Qwen's embedding space. These are unrelated coordinate systems. Feed one to the other and the LLM sees noise — and the gradients it produces are also noise.

If everything were trainable at that moment, those noisy gradients would flow into the LLM and start degrading a model that cost enormous amounts to train. The freeze is a protective measure: the only thing that can change is the mapping, so the only thing that can be learned is the mapping.

Within stage 1 there is a further ordering: "both initially focusing on training their respective adapters before training the encoders". So it is a three-level thaw — adapter, then encoder, then everything.

1a. Adapter only
A small projection learns the coordinate change. Cheapest possible way to get the two spaces talking; nothing valuable is at risk.
↓ alignment is roughly right
1b. Adapter + encoder
Now the encoder can adapt what it extracts, not just how it is projected. Whisper's features were optimized for transcription; the LLM wants features for understanding.
↓ encoders now produce LLM-useful features
2. Everything unfrozen
The LLM can finally reorganize to accommodate the new modalities. 1.2 trillion new multimodal tokens.
3. Long context
8,192 → 32,768. Long audio, long video. The positional machinery from Ch 3 is stretched to its working range.
Read the token budget as a statement of priorities. Stage 2 adds 800B image/video tokens, 300B audio tokens, and 100B video-with-audio tokens. The video-with-audio number is the smallest by a factor of eight — and it is the data for the single hardest capability, the one TMRoPE exists to enable. Aligned audio-visual data is scarce, expensive, and hard to caption. That 100B figure is the honest ceiling on how well the alignment can possibly have been learned, and it is worth remembering when Chapter 9 reports the authors' own note that "audio-video collaborative understanding" remains an open problem.

Why 8,192 first, then 32,768?

Attention cost is quadratic in sequence length. Quadrupling the context multiplies attention cost by sixteen. Training the bulk of the run at 8,192 and only extending at the end is a straightforward compute allocation: you buy most of your learning cheaply and spend the expensive long-context budget only on the capability that requires it.

And 32,768 is not arbitrary either. Put Chapter 2's rates against it: at 448 px and 2 fps, one second of video-with-audio is 512 + 25 = 537 tokens. So 32,768 tokens is about 61 seconds of video-with-audio. A minute. That is the real meaning of the context extension — it is what lets the model hold a whole short clip.

Natural language prompts instead of tag hierarchies

A small change with large consequences: "We replace the hierarchical tags with the natural language prompts following Qwen2-Audio, which can improve better generalization ability and better instruction following ability."

The older convention (Qwen-Audio) marked tasks with structured tags — a hierarchy of special tokens saying "this is transcription, in English, with timestamps". Qwen2-Audio replaced them with plain instructions: "Transcribe the English speech." The consequence is that task specification stops being a closed vocabulary the model was trained on and becomes ordinary language the model already understands. Unseen tasks become expressible, which is exactly what "generalization" means here.

Post-training the Thinker

Brief and conventional: instruction fine-tuning in ChatML format, on "pure text-based dialogue data, visual-modality conversation data, audio-modality conversation data and mix-modality conversation data".

Four data types, and the fourth is the one that matters — conversations that mix modalities within a single exchange are the only data that teaches the model to relate what it heard to what it saw at instruction-following time.

Thinker's post-training is conventional. Talker's is not, and it is where the most transferable technique in the paper lives.

Post-training the Talker — three stages of a different kind

Here the paper gets specific and interesting. "We introduced a three-stage training process for Talker… In the first stage, we train Talker to learn context continuation. The second stage utilized DPO to enhance the stability of speech generation. In the third stage, we applied multi-speaker instruction fine-tuning to improve the naturalness and controllability of the speech responses."

Stage A — in-context learning (context continuation)

"we perform a speech continuation task through next-token prediction, leveraging an extensive dataset of dialogues that incorporate multimodal contexts and spoken responses. Talker learns to establish a monotonic mapping from semantic representation to speech, while also acquiring the ability to express speech with diverse attributes that are contextually appropriate, such as prosody, emotion, and accent."

Two things being learned at once, and they are worth separating:

Plus one defensive technique: "we implement timbre disentanglement techniques to prevent the model from associating specific voices with infrequent textual patterns." Without it, a model trained on many speakers will pick up spurious correlations — if the only person in the corpus who said "quokka" had a Scottish accent, the model learns that "quokka" is Scottish. Disentangling timbre from content breaks the shortcut.

Stage B — DPO for stability

"To broaden the coverage of speakers and scenarios, the pretraining data inevitably contains label noise and pronunciation errors, leading to model hallucinations. To mitigate this issue, we introduce a reinforcement learning phase to improve the stability of speech generation."

The paper writes the DPO objective explicitly — equation (1). Here it is with every symbol defined:

LDPO(Pθ; Pref) = −E(x, yw, yl) ~ D [ log σ( β log ( Pθ(yw|x) / Pref(yw|x) ) − β log ( Pθ(yl|x) / Pref(yl|x) ) ) ]
SymbolMeaning here
xThe input sequence — the request together with the response text
ywThe winning generated speech sequence (lower WER, fewer pause errors)
ylThe losing generated speech sequence
PθThe model being trained
PrefThe frozen reference model — the stage-A checkpoint. The anchor that stops the policy drifting.
βHow hard the policy is allowed to move away from the reference. Small β = timid; large β = free to diverge and to degrade.
σThe logistic sigmoid. Turns the margin into a probability, so the loss saturates once a pair is comfortably ordered.
DThe preference dataset of triplets (x, yw, yl)

Read the objective in words. For each pair, compute how much more likely the model makes the good sample relative to the reference, and how much more likely it makes the bad one. Subtract. Push that difference up. Nothing asks the model to make the good sample likely in absolute terms — only more likely than the bad one, relative to where it started. That relative framing is why DPO does not collapse the distribution the way naive fine-tuning on "good" samples does.

The reward function is the clever part, not the algorithm. DPO is off the shelf. What is specific to this paper is what defines a winner: "We rank these samples based on their reward scores associated with word error rate (WER) and the punctuation pause error rate." Both are automatic. Run an ASR system on the generated speech and compare with the intended text — that is WER, and it catches hallucinated, skipped and mispronounced words. Check whether pauses land where the punctuation says — that catches the rhythm failures that make synthetic speech sound broken. No human raters. A preference dataset of any size can be manufactured, which is the only reason a preference phase is affordable here at all.

Does it work? Chapter 9 has the number, and it is the cleanest ablation in the paper: on seed-tts-eval the ICL model scores 1.70 / 2.72 / 7.97 WER on test-zh / test-en / test-hard, and after the DPO stage it scores 1.42 / 2.33 / 6.54. Every set improves, and the hardest set improves most in absolute terms — which is exactly what a stability intervention should do.

Stage C — multi-speaker instruction fine-tuning

"Lastly, we performed speaker fine-tuning on the aforementioned base model, enabling Talker to adopt specific voices and improve its naturalness."

The order matters. Stability is trained before specialization, so the fine-tuned speakers inherit the robustness rather than having to relearn it. Chapter 9's single-speaker table confirms this: the speaker-tuned models keep the base model's low error rates while gaining naturalness.

Six stages across two curricula, each with its own frozen set and its own data. The sim below lets you step through them.

Explore the curriculum

The training curriculum — what is frozen, what is fed, what improves

Top: the component map, with locks. Middle: the data budget for the selected stage. Bottom: for the Talker track, the DPO margin — drag β and watch how far the policy is allowed to move from the reference, and where the loss saturates.

The objective, the reward, and the ordering are all established. What remains is to write the loss the way you would actually run it — by hand on one pair, batched, and as a library call.

Code: the DPO loss, three ways

By hand on one pair, so you can see the numbers move:

python — one preference pair, by hand
import math
beta = 0.1
# log-probabilities of the two candidate speech sequences
lp_w, lp_w_ref = -20.0, -21.0     # winner: model likes it 1.0 nat more
lp_l, lp_l_ref = -25.0, -24.0     # loser:  model likes it 1.0 nat less

r_w = beta * (lp_w - lp_w_ref)      # 0.1 * ( 1.0) =  0.10
r_l = beta * (lp_l - lp_l_ref)      # 0.1 * (-1.0) = -0.10
margin = r_w - r_l                  # 0.20
loss = -math.log(1 / (1 + math.exp(-margin)))
print(round(margin, 3), round(loss, 4))     # 0.2 0.5981

# margin 0 -> loss = -log(0.5) = 0.6931  (no preference learned yet)
# margin 4 -> loss = 0.0181              (comfortably ordered, gradient tiny)

Then batched, as you would actually implement it:

python — batched DPO
import torch, torch.nn.functional as F

def dpo_loss(lp_w, lp_l, lp_w_ref, lp_l_ref, beta=0.1):
    """All args: (B,) summed log-probs of the full speech-code sequence."""
    margin = beta * ((lp_w - lp_w_ref) - (lp_l - lp_l_ref))
    return -F.logsigmoid(margin).mean()

# building D: no human raters needed
def make_pairs(prompts, model, asr, n=8):
    for x in prompts:
        cands = [model.sample_speech(x) for _ in range(n)]
        scored = [(wer(asr(c), x.text) + pause_err(c, x.text), c) for c in cands]
        scored.sort()
        yield x, scored[0][1], scored[-1][1]     # (x, y_w, y_l)

And the library version:

python — the one-liner
from trl import DPOTrainer
DPOTrainer(model=talker, ref_model=talker_icl, beta=0.1,
           train_dataset=pref_ds).train()

Stage 3: what actually changes when the context quadruples

"Train at 32,768 instead of 8,192" sounds like changing a constant. It is not, and the reason connects directly back to Chapter 3.

Recall RoPE's frequency ladder: pair i rotates at θi = 10000−2i/d per position. The slowest pairs are the ones that encode coarse, document-scale position — and at a training length of 8,192 those pairs have only ever been rotated through a limited range of angles. Positions beyond 8,192 present the model with rotation angles it has literally never seen.

So extending the context is not a matter of allowing a longer buffer. It is a matter of teaching the model what the far end of the frequency ladder means. That requires actual training data at that length, which is why stage 3 exists as a separate stage with its own corpus of "long audio and long video data".

Three things change together:

What changesWhy it must
Data lengthThe slow rotary pairs need examples at their full range, or long-range position is untrained
Attention cost per sampleQuadratic: 4× the length is 16× the attention compute. This is why it is a short final stage rather than the whole run.
What TMRoPE has to span32,768 tokens of video-with-audio is about a minute, so the temporal IDs now range over ~1,500 ticks in a single sequence

The third row is specific to this model. In a text-only LLM, extending context stretches one position axis. Here it stretches the temporal axis of a three-axis scheme, while the h and w axes stay bounded by image dimensions. The axes are being asked to generalize by very different amounts, and only the temporal one is under real pressure.

What the paper says and does not say here. It reports the extension and that "experimental results indicate that our data shows significant improvement in supporting long sequence data". It does not say whether any RoPE scaling technique (position interpolation, YaRN, or similar) was applied, or whether the extension was pure continued training at the longer length. For a reader implementing this, that is the gap to fill from the released config rather than from the report.

Three questions remain about the curriculum: why an adapter is sufficient to bridge two representation spaces, why DPO rather than the alternatives, and what a preference pair actually looks like when the reward is automatic.

Why an adapter works at all — the projection argument

"Train a small adapter to align the spaces" is a sentence everyone nods at. It deserves an actual argument, because the reason it works is not obvious and the reason it sometimes fails is instructive.

The audio encoder emits vectors of some dimension da; the LLM consumes vectors of dimension dm. The adapter is a learned map between them — in the simplest case a matrix W of shape (dm, da) plus a bias.

Why should any linear map suffice? Because of what the two spaces have in common. The encoder's output already separates the things that matter — different phonemes, different speakers, different sound events land in different regions. The LLM's embedding space also separates the things that matter. Neither space is arbitrary; both are organized by the same underlying structure of the world. What differs is the coordinate system: which direction means what.

Changing coordinate systems is exactly what a linear map does. So the adapter's job is a rotation-and-rescale, not a re-derivation of meaning, and rotations are cheap to learn.

encoder output   (T, da)  →  W (dm, da)  →  (T, dm)   sits in the LLM's sequence like any token embedding

Where it breaks. When the encoder does not already separate what the LLM needs. Whisper's encoder was trained for transcription, so it separates phonemes beautifully — and has comparatively little reason to separate, say, a violin from a cello, or a sarcastic tone from a sincere one. No linear map can create a distinction the input does not contain.

That is exactly why stage 1 unfreezes the encoder after the adapter: to let the encoder start representing things it was never asked to represent. And it is why stage 2 unfreezes everything: to let the LLM reorganize around what it is now receiving.

Read the three-level thaw as an information argument, not a compute one. Adapter first, because the space might already contain what we need and that is the cheapest hypothesis to test. Encoder next, because if it does not contain it, only the encoder can add it. LLM last, because it is the most valuable and the most expensive thing to disturb, and it should only move once its inputs have stopped being noise. Each stage unlocks exactly the parameters that can fix the problem the previous stage could not.

Two questions remain about the Talker curriculum: why these three stages, and why in this order. The second is the more interesting one, because the ordering is doing real work.

The order of the Talker stages is an argument

ICL, then DPO, then speaker fine-tuning. Any other order would be worse, and saying why makes the design legible.

OrderWhat goes wrong
DPO before ICLThere is nothing to prefer between — the model cannot yet produce two plausible candidates, so the preference signal is noise.
Speakers before DPOEach fine-tuned voice would inherit the base model's instability and have to be re-stabilized separately, multiplying the work by the number of voices.
Speakers before ICLThe model would learn specific voices before learning the general semantics-to-speech mapping — specializing before it can generalize.
ICL → DPO → speakersEach stage builds on a capability the previous one established, and the final specialization inherits everything underneath it.

Chapter 9's single-speaker table is the evidence: the four fine-tuned speakers keep content-consistency error rates near the RL base model's while gaining naturalness. Stability was not re-earned per voice; it was inherited.

Why DPO and not something else

The stability problem is: the pretraining data has label noise and pronunciation errors, and the model hallucinates. Three families of fixes were available. Compare them on this specific problem.

MethodWhat it needsWhy it fits or does not
More SFT on clean dataA clean corpusDoes not exist at the required scale — the noise is why broad speaker and scenario coverage was possible. Filtering hard shrinks coverage.
PPO with a reward modelA learned reward model, a value head, an on-policy loopWorks, but you must train and maintain a reward model, and the loop is expensive and unstable. Overkill when the reward is already computable.
DPOPairs (winner, loser) and a frozen referenceFits. The reward is automatic (WER + pause errors), so pairs cost only sampling; no reward model, no value head, no on-policy machinery.

The deciding factor is the fourth column of the DPO row: the reward is automatic. When you can score a sample without a human and without a learned model, the expensive parts of preference learning evaporate. DPO then reduces to a supervised loss over pairs.

The pattern to steal from this whole chapter. Freeze what is valuable and expensive; train what is cheap and misaligned; unfreeze in the order of increasing risk. Then, once the model is capable, use automatic rewards to fix stability, and only then specialize. Every stage in both curricula follows that rule, and it generalizes to any project where you are attaching new capabilities to an expensive pretrained artifact.

Scoring a pair by hand

Make the reward concrete. Target text: "The meeting starts at four, not five." The model generates two candidates; we run an ASR system on each.

Candidate 1Candidate 2
ASR transcript"The meeting starts at four, not five.""The meeting starts at four not not five."
Reference words77
Substitutions / deletions / insertions0 / 0 / 00 / 0 / 1 (a repeated "not")
WER = (S + D + I) / N0 / 7 = 0.0001 / 7 = 0.143
Expected pauses (from punctuation)1, after "four"1, after "four"
Pauses actually produced1, correctly placed2 — one after "four", one spurious before the repeat
Pause error rate0 / 1 = 0.0001 / 1 = 1.000
Combined score (lower better)0.0001.143
Roleyw — the winneryl — the loser

The repeated word is the classic autoregressive audio failure — the model loops. WER catches it; a naturalness metric might not, because a fluent repetition can sound perfectly natural. Pairing the two reward terms covers two different failure modes: WER catches what was said, pause-error rate catches how it was timed.

Now feed that pair into the loss from the section above. If the model currently assigns the loser a higher probability than the winner, the margin is negative, the sigmoid is below 0.5, and the loss is above 0.693 — a large gradient pushing the ordering the right way. Once the winner is comfortably preferred, the margin is large, the sigmoid saturates, and the gradient goes quietly to zero. The objective stops caring about pairs it has already fixed, which is precisely the behaviour you want from a stability intervention.

Inline check. Why rank generated samples against each other instead of training the model to imitate the reference recording? Because the reference is one speaker's rendition with one particular timing, and imitating it re-imposes exactly the alignment constraint Chapter 5 celebrated removing. Ranking the model's own outputs teaches "be less like your bad self" without teaching "be exactly like this recording". Preference learning preserves the freedom that supervised imitation destroys.
Why is the LLM frozen during the first pre-training stage?
In Talker's DPO stage, what determines which of two generated speech samples is the "winner"?

Chapter 9: Results, Limits, and What Comes Next

Chapter 0 wrote down four claims and their falsification tests. Time to collect the evidence — and then to be honest about what the paper does not show.

Claim 1: adding a mouth did not cost the eyes

The comparison is against Qwen2.5-VL-7B, the dedicated vision-language model that shares this model's vision encoder. If omni-training degraded vision, it would show here.

BenchmarkQwen2.5-VL-7BQwen2.5-Omni-7BΔ
MMMU (val)60.059.2−0.8
MMMU-Pro (overall)37.636.6−1.0
MathVista (testmini)68.267.9−0.3
MathVision (full)25.125.0−0.1
MMBench-V1.1-EN82.681.8−0.8
MMStar63.964.0+0.1
RealWorldQA68.570.3+1.8
MME-RealWorld (en)57.461.6+4.2
TextVQA (val)84.984.4−0.5
DocVQA (test)95.795.2−0.5
ChartQA (test avg)87.385.3−2.0
OCRBench_v2 (en)56.357.8+1.5

Mostly within a point, four benchmarks improved, worst case two points on ChartQA. And on video the picture is better still — Video-MME with subtitles 72.4 against 71.6, MVBench 70.3 against 69.6, EgoSchema 68.6 against 65.0, with only Video-MME without subtitles slightly behind (64.3 against 65.1). Grounding likewise: Refcoco val 90.5 against 90.0, and open-vocabulary detection on ODinW 42.2 against 37.3.

Claim 1 survives. Notice which benchmarks improved: RealWorldQA, MME-RealWorld, EgoSchema — the ones closest to embodied, egocentric, real-scene understanding. Plausibly the audio-visual training data helped exactly where the visual world is messy and temporal.

Claim 2: it beats the audio model it descends from

TaskWhisper-large-v3Qwen2-AudioQwen2.5-Omni-7B
Librispeech test-clean / test-other (WER, lower better)1.8 / 3.61.6 / 3.61.8 / 3.4
Common Voice 15 en / zh / yue / fr (WER)9.3 / 12.8 / 10.9 / 10.88.6 / 6.9 / 5.9 / 9.67.6 / 5.2 / 7.3 / 7.5
Fleurs zh / en (WER)7.7 / 4.17.5 / —3.0 / 3.8
Wenetspeech test-net / test-meeting (WER)5.9 / 7.7
CoVoST2 en-de / de-en / en-zh / zh-en (BLEU, higher better)29.9 / 35.2 / 45.2 / 24.430.2 / 37.7 / 41.4 / 29.4
MMAU sound / music / speech / avg54.95 / 50.98 / 42.04 / 49.2067.87 / 69.16 / 59.76 / 65.60
Meld emotion recognition (acc)0.5530.570
VocalSound classification (acc)0.9390.939
VoiceBench average55.3574.12

The MMAU row is the striking one. Audio reasoning — not recognition — jumps from 49.20 to 65.60 average, beating Gemini-Pro-V1.5's 54.90. Recognition improved incrementally; reasoning improved by a third. That is the signature of a better language model attached to the same ears, which is exactly what an omni model is.

Claim 3: the headline — speech instructions work like text instructions

Take pure-text benchmarks. Convert the instructions to speech. Re-run. The gap between speech-in and text-in is the price of talking to the model instead of typing.

Benchmark (speech instructions)Qwen2-7B with textQwen2-AudioQwen2.5-Omni-7BGap to text
MMLU69.333.265.6−3.7
CEval78.438.661.1−17.3
IFEval53.315.641.7−11.6
GSM8K82.318.485.4+3.1
Math23K92.323.087.1−5.2
Math40175.520.462.2−13.3

Look at the Qwen2-Audio column first, because it is the control. 33.2 on MMLU. 18.4 on GSM8K. Speaking to that model destroyed roughly three quarters of its reasoning ability. Every "audio LLM" before this had a version of that column.

Now Qwen2.5-Omni: 65.6 and 85.4. On GSM8K the spoken version beats the text baseline it is compared against. The gap has gone from catastrophic to, on several benchmarks, small.

Three caveats you must attach to this table, and the paper supplies all three. (1) The comparison model is Qwen2-7B, not Qwen2.5-7B — the newer, stronger sibling. Against Qwen2.5-7B's text scores the gaps would be wider. (2) The asterisk in the paper's table means "approximately 90% of text instructions suitable for speech are used" — instructions that cannot sensibly be spoken (tables, code blocks) were dropped, so this is the speakable subset. (3) The remaining gaps are not uniform: CEval −17.3 and Math401 −13.3 are large. Knowledge-dense and symbol-dense material still suffers when it arrives as sound. The headline is real and it is not "solved".

Why does GSM8K improve at all? A reasonable hypothesis: grade-school word problems are narrative, exactly the register speech handles well, and the 2025-era model simply has better math than the 2024-era text baseline. The comparison is across model generations as well as across modalities, and the paper does not disentangle them.

Claim 4: streaming speech output did not cost quality

Zero-shot TTS on seed-tts-eval. WER measures content consistency (did it say the right words); SIM measures speaker similarity to the prompt voice.

SystemWER test-zhWER test-enWER test-hardSIM zh / en / hard
Seed-TTS (ICL)1.112.247.580.796 / 0.762 / 0.776
Seed-TTS (RL)1.001.946.420.801 / 0.766 / 0.782
MaskGCT2.272.6210.270.774 / 0.714 / 0.748
E2 TTS1.972.190.730 / 0.710 / —
F5-TTS1.561.838.670.741 / 0.647 / 0.713
CosyVoice 21.452.576.830.748 / 0.652 / 0.724
CosyVoice 2-S (streaming)1.452.388.080.753 / 0.654 / 0.732
Qwen2.5-Omni-7B (ICL)1.702.727.970.752 / 0.632 / 0.747
Qwen2.5-Omni-7B (RL)1.422.336.540.754 / 0.641 / 0.752

Three readings.

The DPO stage works. ICL → RL improves every column: 1.70→1.42, 2.72→2.33, 7.97→6.54. That last one is 1.43 points of absolute WER on the hardest set, from a preference phase built entirely on automatic rewards.

It beats the streaming competitor on the hard set. Against CosyVoice 2-S — the streaming variant, the fair comparison — 6.54 against 8.08 on test-hard. Against the non-streaming CosyVoice 2, 6.54 against 6.83. A streaming system beating a non-streaming one on the hardest set is the result this claim needed.

Seed-TTS still wins overall. 1.00 / 1.94 / 6.42, and higher speaker similarity everywhere. Qwen2.5-Omni is not the best TTS in the world; it is a very good TTS that is also a full multimodal LLM and streams. The paper says "outperforms most existing streaming and non-streaming alternatives", and "most" is doing honest work.

The single-speaker table closes the loop: after speaker fine-tuning, content consistency stays at human-adjacent levels (human recordings score 1.25 zh / 2.14 en; the four fine-tuned speakers score 1.28–1.37 zh and 1.83–2.13 en), and naturalness (NMOS) lands in the 4.48–4.62 range against human recordings at 4.51 zh / 4.46 en.

The multimodal headline: OmniBench

OmniBench requires reasoning over image, audio and text simultaneously — the capability that only exists if the alignment machinery of Chapters 3 and 4 actually works.

ModelSpeechSound EventMusicAvg
Gemini-1.5-Pro42.67%42.26%46.23%42.91%
MIO-Instruct (7B)36.96%33.58%11.32%33.80%
AnyGPT (7B)17.77%20.75%13.21%18.04%
video-SALMONN (13B)34.11%31.70%56.60%35.64%
UnifiedIO2-xlarge (3.2B)39.56%36.98%29.25%38.00%
UnifiedIO2-xxlarge (6.8B)34.24%36.98%24.53%33.98%
MiniCPM-o40.5%
Baichuan-Omni-1.542.9%
Qwen2.5-Omni-7B55.25%60.00%52.83%56.13%

56.13% against a previous best of 42.91%. More than thirteen points, and against Gemini-1.5-Pro. This is the number that most directly validates TMRoPE — the benchmark is precisely "can you use two modalities at once", and the mechanism for using two modalities at once is the one thing this paper invented for perception.

The bill: what omni-training cost the text model

The paper reports this openly, which is to its credit. Qwen2.5-Omni-7B "generally falls between Qwen2-7B and Qwen2.5-7B".

BenchmarkQwen2-7BQwen2.5-7BQwen2.5-Omni-7B
MMLU-Pro44.156.347.0
MMLU-redux67.375.471.0
LiveBench29.235.929.6
GPQA34.336.430.8
MATH52.975.571.5
GSM8K85.791.688.7
HumanEval79.984.878.7
MBPP67.279.273.2
MultiPL-E59.170.465.8
LiveCodeBench23.928.724.6

Beats Qwen2-7B on most rows; loses to Qwen2.5-7B on all of them. MMLU-Pro is 9.3 points behind its text-only sibling. That is the omni tax, stated plainly. Whether it is worth paying depends entirely on whether you need the model to hear and speak.

Explore all the results

Benchmark explorer — every table in the paper, one view at a time

Pick a category. Bars are drawn to the scale of the metric; for WER (lower is better) the axis is inverted and labelled as such. Every number is transcribed from the report.

Limitations — what the paper does not show

LimitationEvidenceWhy it matters
No latency measurementsInitial packet latency is the stated design goal; no milliseconds appear anywhere in the reportEvery streaming claim is architectural, not empirical. You cannot compare it to any other system on the metric it optimizes.
Not full duplexTalker's "dual track" is text plus codes, not model plus user. No barge-in, overlap, or backchannel evaluationReal conversation is interruptible. Systems like Moshi are evaluated on exactly this and Qwen2.5-Omni is not.
Speech-gen evaluated as TTS only"Due to the lack of relevant assessments, the evaluation of speech generation focuses primarily speech generation given texts, similarity to text-to-speech"The interesting claim — that context-conditioned prosody is better because Talker sees hidden states — is not measured. seed-tts-eval cannot see it.
The omni tax on textMMLU-Pro 47.0 against Qwen2.5-7B's 56.3A real cost, honestly reported, and a reason to keep a text-only model around.
Speech-instruction gaps remainCEval −17.3, Math401 −13.3, IFEval −11.6 against the text baselineThe headline claim is true on average and false in the knowledge-dense tail.
Codec details unpublishedqwen-tts-tokenizer's rate, codebook structure and vocabulary are not givenNot reproducible; and the codec is where most speech-quality ceilings live.
Authors' own open problems"video OCR and audio-video collaborative understanding" are named as critical issues that have often been overlookedThe second one is the capability TMRoPE exists for. They are saying, in the conclusion, that it is not finished.
The most valuable sentence in the conclusion. "In the development of the model, we have identified several critical issues that have often been overlooked by researchers in previous academic studies, such as video OCR and audio-video collaborative understanding. Addressing these challenges necessitates collaboration between the academic and industrial sectors, particularly in building comprehensive evaluation benchmarks and research datasets." Translated: we cannot tell how good our audio-visual alignment really is, because no one has built the benchmark. OmniBench is a start, not a verdict. That is an unusually honest thing for a technical report to say about its own headline mechanism.

Cheat sheet — everything worth memorizing

QuantityValueWhere it comes from
Audio sample rate16 kHzCh 2
Mel channels / window / hop128 / 25 ms / 10 msCh 2
Mel frame rate100 per second1 / hop
Audio token duration40 ms (25 tokens/s)100 → conv/2 → pool/2
Vision patch size14 pxCh 2
Vision token merge2×2 → 1 (MLP)Ch 2
Visual tokens per frame(px / 14 / 2)2Ch 2
Vision encoder size≈675M parametersCh 2
Text vocabulary151,643 regular tokens (byte-level BPE)Ch 2
An image istwo identical framesCh 2
One TMRoPE temporal ID40 msCh 3
Position triple(t, h, w) — temporal, height, widthCh 3
Text / audio position ruleAll three components identicalCh 3
Video position rulet from wall-clock time; h, w from the merged gridCh 3
Modality chainingstart = max position ID of previous modality + 1Ch 3
Interleave chunk2 seconds, visual first then audioCh 4
Audio encoder attentionBlock-wise, blocks of 2 seconds (50 frames)Ch 6
DiT receptive field4 blocks = 2 lookback + current + 1 lookaheadCh 6
Codec → melFlow-matching DiTCh 6
Mel → waveformModified BigVGAN, chunkedCh 6
Pretrain stage 2 budget800B image/video + 300B audio + 100B video-with-audioCh 8
Context length8,192 → 32,768Ch 8
Talker post-trainingICL → DPO (WER + pause-error reward) → speaker FTCh 8
OmniBench average56.13% (previous best 42.91%)Ch 9
seed-tts-eval WER after RL1.42 / 2.33 / 6.54Ch 9
MMAU average65.60 (Qwen2-Audio 49.20)Ch 9
VoiceBench average74.12Ch 9
Speech-MMLU65.6 (Qwen2-Audio 33.2; text baseline 69.3)Ch 9

The cheat sheet closes the technical content. What remains is placement: where this model came from, what it points at, and which lessons on this site sit on either side of it.

Where this sits in the lineage

The road to a talking model — and where it goes next

Tap a node to see what it contributed. The highlighted path is the direct ancestry of Qwen2.5-Omni; the branches are the contemporaries that made different bets.

Tap a node.

Connections

DirectionLessonWhy
← PrerequisiteQwen2-AudioThe direct ancestor: Whisper encoder + adapter + Qwen LLM, natural-language prompts replacing tag hierarchies. Everything on the audio-understanding side of this paper is inherited from it.
← BackgroundWhisperThe audio encoder's initialization, and the source of the 128-mel / 25 ms / 10 ms front end.
← BackgroundNeural audio codecsWhat qwen-tts-tokenizer is a member of — residual quantization, streaming causal decoders, the code-rate/quality trade.
↔ ContrastMoshiThe other answer to real-time voice: one model, dual stream, full duplex. Read them together; the disagreement is the education.
↔ ContrastTTS architecturesWhat Talker would be if it had no access to hidden states — and why forced alignment used to be mandatory.
→ NextAudio LLMsThe wider family: encoder+adapter+LLM, dual-encoder routers, audio tokens inside the LLM, and how they are evaluated.
→ NextDuplex speech-language-actionWhere this goes: synchronized speech, language and action — voice driving agentic tool use in real time.
The one sentence to remember from the results. Speech-instruction MMLU went from 33.2 to 65.6 against a text baseline of 69.3 — which is the moment talking to a model stopped being a degraded way of using it.

The last word

Strip away the benchmarks and one idea remains. For a decade, systems that spoke were assembled from stages, and every stage boundary was a place where information was thrown away and time was spent. Qwen2.5-Omni's argument is that both losses have the same cure: make the boundaries continuous and make them small. Continuous, so the plan survives the crossing — hidden states, not strings. Small, so nothing has to wait for a whole utterance — two-second blocks, four-block windows, forty-millisecond ticks.

That is why TMRoPE and the Thinker-Talker split belong in the same paper even though one is about perception and the other about generation. They are the same move, applied at the two ends of the machine.

Four claims, four verdicts. What follows is the residue — the results that did not fit the abstract but change how you read it.

Numbers that did not make the headline

A few results worth pulling out of the tables, because each says something the abstract does not.

ResultNumberWhat it means
Fleurs zh ASR3.0 WER against Whisper-large-v3's 7.7The audio encoder was initialized from Whisper and then trained further — on Mandarin it more than halved its parent's error rate.
MMAU music69.16 against Qwen2-Audio's 50.98The largest single jump in the audio tables, on the subset furthest from speech. Broad audio training, not just speech training.
VoiceBench AdvBench99.42Safety behaviour survives the switch to spoken input almost perfectly — a real worry for voice interfaces, quietly addressed.
ODinW open-vocabulary detection42.2 mAP against Qwen2.5-VL-7B's 37.3The omni model beats its vision-specialist sibling at detection in the wild. Multimodal training helped grounding.
PointGrounding66.5 against 67.3And loses, slightly, at point grounding. The gains are not uniform, which is what an honest table looks like.
Voxpopuli-V1.0-en5.8 against Llama-3-70B's 5.7A 7B omni model within 0.1 WER of a 70B model on European parliamentary speech.
CoVoST2 en-zh41.4 BLEU against Qwen2-Audio's 45.2One of the few audio regressions. Speech translation into Chinese got worse; the report does not say why.

The last row deserves a moment. It would have been easy to omit — three other CoVoST2 directions improved. Including a regression in a table you control is a small signal of good faith, and it is the sort of thing to look for when calibrating how much to trust the rest.

One caution about reading these tables. Every number above is a single run with no error bars, no seeds, and no significance testing — the norm for technical reports, but worth naming. Differences under roughly a point should be read as "comparable", not as wins. That applies in both directions: OCRBench v2's +1.5 is as soft as ChartQA's −2.0. The results that survive this discipline are the large ones — OmniBench +13.2, MMAU +16.4, speech-MMLU +32.4 over Qwen2-Audio — and those are exactly the ones the abstract leads with.

If you wanted to replicate or extend this

Five experiments the paper makes possible and does not run. Each is a plausible follow-up.

  1. Measure TTFA properly, using Chapter 7's checklist, and publish the sweep over block sizes and lookahead. This is the missing number and it is cheap to produce.
  2. Ablate the hidden-state handoff. Train a Talker on sampled tokens alone and compare on a contextual prosody benchmark — which you would first have to build, since none exists. That absence is itself the finding.
  3. Sweep the interleave chunk size. The paper asserts 2 seconds without an ablation. Does OmniBench move at 1 s or 4 s? If not, the choice is free and the delay could be halved.
  4. Test the TMRoPE claim directly. Construct clips where the answer depends on precise audio-visual synchrony — which sound accompanied which gesture — and compare against sequence-index positions. AV-Odyssey is a start; a synthetic, controllable version would be sharper.
  5. Add duplex. Take the Thinker-Talker split and give Talker a second input stream carrying the user's live audio. Whether the split survives the addition is a genuinely open architectural question, and it is where the next generation lives.
What to take away about reading systems papers. The most useful reading habit this lesson has tried to build is the reflex of asking, at every number: what does this make possible, and what does it cost? Forty milliseconds buys audio-visual alignment and costs nothing. Two seconds buys cross-modal context and costs two seconds of delay. One block of lookahead buys clean block boundaries and costs one block of delay. Four blocks of receptive field buys prosodic continuity and costs only compute. Every one of those is a sentence of the form "X buys Y at the price of Z", and a systems paper is, in the end, a list of them.
On the speech-instruction benchmarks, what makes the Qwen2-Audio column the important control?
Which of these is a genuine limitation of the paper, rather than a strength?