Karchkhadze & Dubnov (UC San Diego / IRCAM), 2026

Real-Time Musical Co-Performance
with a Look-Ahead Diffusion Model

A latent diffusion model jams with a live human — generating a bass line for a drummer in real time by generating ahead of the beat to hide its own inference latency, distilled to 5.4× faster, and wired into MAX/MSP over OSC.

Prerequisites: Diffusion models + Latent autoencoders
11
Chapters
6
Simulations

Chapter 0: The Problem

You sit down at a drum kit. A bassist is supposed to join you. Except there is no bassist — there's a neural network. You hit the first beat, and you need the bass to land with you, on the next downbeat, not half a second late. That half second is an eternity in music. A groove that drags by 200 ms doesn't feel "a little behind"; it feels broken.

That is the whole problem this paper attacks: real-time human–AI musical co-performance. A human plays live; a generative model must produce a complementary instrumental part — bass for a drummer, say — and have it sounding now, locked to the human's timing.

The core question: Modern music-generation models are huge and slow — a single generation can take a second or more. Audio playback, by contrast, demands a new buffer of samples every few milliseconds. How can a model that is three orders of magnitude too slow still produce sound that arrives on time, every time?

Why this is brutally hard — the latency gap

Digital audio is processed in tiny buffers. A typical host (a DAW, or here MAX/MSP) hands the soundcard a block of maybe 64 samples — about 1.5 ms at 44.1 kHz — and that block must be ready before it plays. Miss the deadline and you get a click, a dropout, silence.

Now look at a diffusion model. To denoise one chunk of audio it might run ten neural-network forward passes through a 257-million-parameter U-Net. The paper measures this at roughly 1000–1400 ms per chunk on real hardware. The model is literally a thousand times slower than the buffer it is supposed to fill.

So "generate the next sound when the speaker asks for it" is hopeless. The model cannot keep up on demand. Something structural has to change.

A worked sense of the gap

QuantityValueWhy it matters
Host audio buffer (δ)~1.5 msSound must be ready before each buffer plays
One diffusion generation (d)~1000–1400 ms10 forward passes through a 257M-param U-Net
Ratio d / δ~800×The model is hopelessly slower than playback
Human "in-time" tolerancetens of msBeyond this, the groove audibly drags

The numbers say it plainly: you cannot generate audio at the moment it is needed. The only way out is the same trick a human musician uses — anticipate. Generate the bass note before the downbeat arrives, so it's already sitting in the buffer when playback reaches it.

And there's a second wall: the tools don't talk

Even if you had a fast model, there's an infrastructure chasm. State-of-the-art models live in Python on remote GPU servers. Musicians live in MAX/MSP, Ableton, hardware controllers — real-time audio environments. Python can't natively do low-latency audio I/O, and there's no standard way to plug an instrument into a Python model and get a musically aligned response back. The paper has to solve both: the anticipation problem and the plumbing problem.

Why can't a diffusion model generate accompaniment "on demand," the moment each audio buffer is needed?

Chapter 1: The Key Insight

A human ensemble doesn't react instantly either — nerve and muscle have latency too. Yet musicians stay locked together. How? They anticipate: each player continuously predicts what's about to happen and commits to a note slightly ahead of needing it, trusting everyone to land in the shared groove.

The paper steals exactly this. Instead of generating the audio that plays now, the model generates audio for a moment w steps in the future. While that future chunk waits in a buffer, playback consumes the previously generated chunk. By the time playback reaches the future chunk, the model has already finished it. The slow inference happens in the gap the look-ahead created.

The insight: Decouple the generation timeline from the playback timeline. If the model always works on a segment that won't be heard until w step-sizes from now, then as long as one generation finishes within one step (d ≤ T·r), playback never starves — even though d is a thousand times the buffer size.

The three pillars that make it real

Look-ahead sliding window
Train the model to predict a future segment from partial context — generating ahead so inference hides behind playback.
Consistency distillation
The deeper the look-ahead, the worse the quality — so make the model faster instead. Distill 10 forward passes down to 2 (5.4× speedup).
MAX/MSP client–server
A C++ MAX external streams audio chunks over OSC/UDP to a Python GPU server and writes predictions back into a shared buffer.

What the whole system is, in one breath

A human plays into MAX/MSP. MAX captures the live audio, sums the non-target instruments into a context mixture, and ships it over OSC/UDP to a Python server. The server encodes context into Music2Latent's compressed latent space, masks out the future region it must invent, and runs a latent diffusion model (or its distilled consistency twin) to inpaint/outpaint the target stem. The decoded audio streams back and gets written into the buffer one step ahead of playback. Repeat forever. We will build every piece — and you'll see exactly which tensor flows where.

What is the central trick that lets a slow model still play in time?

Chapter 2: Background — Stems, Context, and the Task

Before the streaming machinery, let's nail the data. A song is a sum of stems — isolated instrument tracks. The paper works with four: bass, drums, guitar, piano, from the Slakh2100 dataset (synthesized multi-track MIDI rendered to audio at 44.1 kHz).

Write the mixture as the sum of S mono stems:

xmix = ∑s=1..S xs,   xs ∈ RT·sr

Symbol by symbol, with intuition:

The task: predict one stem from the rest

Accompaniment generation means: given everything except the target instrument, synthesize the target. The context is the sum of all the other stems:

s = f(xcontext(s)),   xcontext(s) = ∑i≠s xi

Read it as: "the predicted bass s is a function f of the mixture of drums + guitar + piano." During training, one target stem and its one-hot label s are picked at random; the context is the sum of the remaining stems. This is exactly the live scenario: the drummer plays, and the model fills in the bass.

Why sum the context into one signal instead of feeding stems separately? Two reasons. First, it matches what a microphone hears in a room — one mixed signal, not isolated tracks. Second, it makes the model model-agnostic to instrument count: the network always sees "one context channel + one target channel," whether the room has two players or ten. (The paper notes an alternative mode that sends stems independently, left for future work.)

The shapes we'll be tracing

StageTensorMeaning
6-s context mixture[1, 264600]mono audio at 44.1 kHz
After Music2Latent encode[1, 64, 64]latent image: Tz=64 time × Fz=64 freq
Network input (concat)[2, 64, 64]noisy target latent + context latent, channel-stacked
Network output[1, 64, 64]denoised target stem latent
After decode[1, 264600]the generated stem waveform
In this paper, what is the "context" the model conditions on to generate the bass stem?

Chapter 3: The Sliding-Window Protocol

The model only sees 6 seconds at a time, but a performance lasts minutes. How do you generate forever from a fixed window? You slide the window forward, generating only the small new slice at each step and reusing what you already made.

Define the step size as T·r, where r ∈ (0, 1] is the fraction of the window advanced each step. Small r = frequent, fine-grained updates; large r = coarse, infrequent ones. At each step the model conditions on the current context plus its own previous prediction shifted forward in time:

s,t = f( xcontext,t(s)  |  SrT(x̂s,t−1) )

Decode it:

So at each step the model inpaints just the new slice and copies the rest. The window advances by T·r and the process repeats — continuous generation, glued together by the overlap.

Why overlap-and-reuse instead of regenerating the whole window? Two payoffs. (1) Speed: you only denoise a small slice (16 latent frames at r=0.25), not all 64 — far less compute per step. (2) Continuity: feeding the previous prediction back as fixed context forces the new slice to musically continue what came before, instead of restarting from scratch and clicking at every seam.

Worked example with real numbers

Take the paper's defaults: T = 6 s, r = 0.25. Then the step size is T·r = 1.5 s (66,150 samples). Each step, the window slides 1.5 s forward; the model regenerates only the newest 1.5 s while the older 4.5 s carry over as fixed context. In the latent grid (64 frames for 6 s), 1.5 s maps to exactly Tz·r = 64×0.25 = 16 latent frames. That whole-number constraint matters — you cannot mask a fractional frame.

Sliding Window — Step Through Generation

Green = audio already known (observed or generated earlier). Red = the new slice the model inpaints this step. Drag r to change step size and hit Advance to slide the window. Notice only the last r·T is ever red.

At step size r = 0.25 with T = 6 s, how much of the window does the model actually regenerate each step?

Chapter 4: Look-Ahead — Three Regimes (the showcase)

Now the heart of the paper. We have a sliding window — but where in that window does the current playback moment sit? That position, controlled by the integer look-ahead depth w, decides everything: how much context the model has, whether it's filling a gap or extending past the edge, and whether real-time is even possible.

Define w ∈ Z as the number of step-sized intervals T·r between the current playback position (t = 0) and the start of the predicted slice (always the last r·T of the window). Three regimes fall out, by the sign of w:

Retrospective (w < 0)
Predicted slice lies entirely in the past. Full context available. This is classic offline accompaniment — the upper bound on quality. Done via inpainting.
Immediate (w = 0)
Predicted slice starts exactly at the current moment. Needs d ≤ δ for real-time — impossible for diffusion, since d ≫ δ.
Look-ahead (w > 0)
Predicted slice starts w steps in the future. Creates a buffer so old audio plays while the next is computed — real-time even when d > δ. Done via outpainting.
The trade-off, stated plainly: Look-ahead buys you time but costs you context. The further ahead you generate, the less observed audio sits inside the window to condition on — the model is increasingly inventing the future blind. At w = 1 the system already operates with a two-step generation horizon: only (1 − (w+1)·r)·T of the window contains known audio. This is the fundamental tension the paper exposes — latency vs. look-ahead depth vs. quality.

Where does the current moment sit? (worked)

The paper's key geometric fact: across all regimes, the current time t=0 sits (w+1)·T·r seconds from the right edge of the window. Walk through it with r=0.25, T=6:

RegimewDistance of t=0 from right edgeKnown context in window
Retrospective−10 · 1.5 = 0 s (right edge)full 6 s — nothing missing
Immediate01 · 1.5 = 1.5 s4.5 s known, 1.5 s future
Look-ahead12 · 1.5 = 3.0 s3.0 s known, 3.0 s future

At w=1, r=0.25, half the window is unobserved future. That's why quality drops in look-ahead — and why the paper constrains r < 0.5 when w=1 (at r ≥ 0.5 the prediction window would fall entirely outside the context).

Look-Ahead Regimes — Reconstructs Figure 2

The bar is the 6-s receptive field. Green = known audio, striped green = the look-ahead buffer (already generated, waiting to play), red = the slice being generated now. The white line is the playback head t=0. Drag w across −1, 0, 1 to see inpainting become outpainting; hit Run to watch playback consume the buffer while the next slice is generated.

Why does generation quality degrade as look-ahead depth w increases?

Chapter 5: The Latent Diffusion Backbone

Diffusion on raw 264,600-sample waveforms would be hopelessly expensive. So the model never touches raw audio during diffusion — it works in a compressed latent space produced by a frozen autoencoder, Music2Latent.

The encoder E maps a mono 6-s clip [1, 264600] to a 2-D latent z ∈ RTz×Fz = [64, 64]. That's a temporal compression of 264600/64 ≈ 4134× — the audio is squeezed down a time axis (64 frames) and projected into a 64-bin frequency-like axis. The decoder D rebuilds the waveform. Both are frozen during diffusion training.

Why freeze the autoencoder? It gives the diffusion model a stable target distribution. If the latent space kept shifting while the diffusion model learned it, you'd be chasing a moving goalpost — the score function would never converge. Freezing E and D means "model q(z)" is a fixed, well-defined problem. The diffusion model inherits a good compression for free and only learns the generative part.

The score-matching objective

The backbone gφ is a SongUNet (DDPM++ style). It's trained by denoising score matching (DSM): corrupt a clean latent with noise, ask the network to recover the clean version. Corruption is additive: zn = z0 + σnε, where ε ∼ N(0,I) and σn is the noise level. The loss:

LDSM(φ) = E [ ‖ zs − gφ(zs + σnε,  s,  σn,  zcontext(s)) ‖22 ]

Symbol by symbol:

The network learns the score z log p(z) — the direction in latent space that points toward cleaner, more plausible audio. Inference follows that field downhill from pure noise to a clean stem via an ODE solver (DPM-2), in just 5 denoising steps (10 forward passes with 2 resamples each).

Latent Diffusion Pipeline — Click a Stage
Click any block to trace the tensor flowing through it.

The diffusion never touches raw audio: it lives entirely on the 64×64 latent grid between encoder E and decoder D (both frozen). Click each stage to see its tensor shapes.

Why does the diffusion model operate on Music2Latent latents instead of raw waveforms?

Chapter 6: Masked Context Conditioning

The sliding-window protocol was defined in the audio time domain, but the diffusion model lives in the latent grid. How do you realize "the future part of the window is unknown" inside a 64×64 latent? With a binary mask that zeros out the future region.

The current moment sits (w+1)·T·r from the right edge (Ch 4). In latent frames, that's Tz·r·(w+1) frames of the right side that are unavailable. Define the context mask:

Mcontext[t, f] = 1 if t < Tz − Tz·r·(w+1),   else 0

The masked context is zcontext ⊙ Mcontext — multiply elementwise, zeroing the future frames. The constraint Tz·r ∈ N (a whole number of latent frames) is why r can't be arbitrary: you cannot mask half a frame.

The clever asymmetry: the context mask and the target mask are different. The context loses (w+1) steps of future (it genuinely doesn't know them). But the target only needs its final r·T slice synthesized — everything before that was generated last step and gets pinned. So the target mask keeps the last Tz·r frames open and fixes the rest:
Ms[t, f] = 1 if t < Tz − Tz·r,   else 0

At inference, the unmasked target region (Ms=1) is filled with last step's prediction and held fixed; the masked region (Ms=0) starts as Gaussian noise and gets denoised. This asymmetric masking is what preserves continuity across steps while regenerating only the truly new audio.

Why randomize r during training?

At training time the masks for r ∈ {0, 0.125, 0.25} and w ∈ {−1, 0, 1} are applied at random. This exposes the model to every degree of missing context, so a single network handles all three regimes at inference — no separate model per look-ahead setting. It's curriculum-by-randomization: the network learns to be robust to "how much future is missing."

Latent Masking — Drag w and r to Reshape the Mask

The 64×64 latent grid (time across, freq down). Green columns = kept (mask=1), red columns = zeroed/regenerated (mask=0). Toggle between the context mask (loses w+1 future steps) and the target mask (only the last step is open). Notice how they differ for the same w, r.

python
import torch

def context_mask(Tz, Fz, r, w):
    """1 where context is known, 0 where future is unavailable."""
    cut = int(Tz - Tz * r * (w + 1))    # whole-frame boundary
    M = torch.zeros(Tz, Fz)
    M[:cut, :] = 1
    return M

def target_mask(Tz, Fz, r):
    """1 = reuse last step (fixed), 0 = the new slice to denoise."""
    cut = int(Tz - Tz * r)
    M = torch.zeros(Tz, Fz); M[:cut, :] = 1
    return M

# r=0.25, w=1, Tz=64 -> context keeps 32 frames, target keeps 48
print(context_mask(64,64,0.25,1).sum(0)[0].item())  # 32
print(target_mask(64,64,0.25).sum(0)[0].item())   # 48
Why are the context mask and the target mask different from each other?

Chapter 7: Consistency Distillation

Recall the trade-off from Ch 4: deeper look-ahead = worse quality. The paper's escape hatch is to attack the other variable — speed. If the model were fast enough, you could use a shallower look-ahead (smaller r), keeping more context and better quality. So: make diffusion faster.

Diffusion is slow because it integrates an ODE over many steps. Consistency distillation (CD) trains a student network gω to jump from any point on the diffusion trajectory directly to the clean data estimate — collapsing the iterative walk into one or two steps.

The intuition: the teacher's denoising trajectory is a smooth curve through latent space — every point on it should map to the same clean endpoint. CD enforces exactly that "consistency": no matter where on the curve you start, the student must land at the same clean answer. Once it has learned this, you don't need to walk the curve step by step — you teleport to the end.

How the student is trained (CTM formulation)

The student is architecturally identical to the teacher. Teacher targets are made by running k ODE solver steps from noise level σn down to σn−k:

s,n−kdif = Solverk( zs + σnε,  s,  σn,  zcontext(s);  gφ )

The student is then trained to match that target in one jump, via the CD loss:

LCD(ω) = ‖ gsg(ω)(ẑn−kdif, σn−k, ...) − gω(zsnε, σn, ...) ‖22

The two terms: a target (the EMA student at the lower noise level) and an estimate (the live student at the higher noise level). Matching them forces consistency across noise levels. The target uses a stop-gradient EMA of the student's own weights — a slowly-updated copy that gives a stable goalpost:

sg(ω) ← stopgrad( μ·sg(ω) + (1−μ)·ω ),   μ = 0.999

Why EMA? If the student chased its own instantaneous output, training would collapse (it could trivially output anything and "match itself"). The slow-moving EMA breaks that feedback loop — it's like a teacher who only slowly revises their answer key. Finally, CD is augmented with the DSM loss for direct data grounding:

L(ω) = LCD(ω) + λDSM·LDSM(ω),   λDSM = 0.7

The payoff — worked from the timing table

The diffusion model runs 5 denoising steps × 2 resamples = 10 forward passes. The CD model uses just 2 consistency steps. On the Paris RTX A6000 (config 2), sampling drops from 480 ms → 88 ms — a 480/88 ≈ 5.4× speedup. That speedup is the difference between meeting and missing the real-time deadline at small step sizes.

Diffusion vs. Distilled — Watch the Sampling Trajectory

From pure noise (top) to clean data (bottom). The orange path is the teacher walking the ODE one small step at a time. The green path is the distilled student teleporting in 2 jumps. Both reach the same clean target — the student just gets there ~5× faster. Drag teacher steps to compare.

What does consistency distillation train the student to do, and why use an EMA target?

Chapter 8: The MAX/MSP Bridge

A fast, look-ahead model is useless if a musician can't plug into it. The second half of the paper is pure systems engineering: connecting a Python GPU model to a live MAX/MSP instrument with minimal latency. The architecture is client–server: MAX is the client; Python is the server; they speak OSC over UDP.

The multi~track external (C++)

The authors wrote a custom MAX external in C++ called multi~track. Why C++ and not a native MAX patch? Because MAX's control-rate messaging would bottleneck the high-throughput audio streaming — dropping to C++ bypasses that constraint. The external is the sole bridge between MAX and the server, managing the whole lifecycle of audio data:

The windowed read/write — implementing w directly

Each prediction cycle is triggered by a predict <curr> message, where curr is the most recently crossed step boundary (a multiple of r·T samples). The external then:

This is where the math becomes machinery. The look-ahead depth w from Chapter 4 is not an abstraction — it is literally the offset where the external writes generated audio into the buffer. Set w=1 and the C++ writes the bass one full step ahead of the playback head. The buffer is the look-ahead.

Chunked OSC with stale-response detection

Audio is sent as fixed-size UDP packets (default 4,410 samples = 0.1 s, giving 15 packets per 1.5-s step). Each OSC message carries a step identifier, a chunk index, and total chunk count. The step ID lets the server discard stale responses — if a new prediction cycle started, results from the old one arriving late are dropped. The chunk index lets the server reassemble samples in order and self-trigger inference once all chunks land.

A fade argument crossfades chunk boundaries: the server returns a few extra samples before the nominal write window, and the external applies a ~20 ms linear fade-in to suppress clicks at the seams between consecutive generated chunks.

The server side — rolling buffers

The Python server holds Music2Latent and the diffusion/CD model on GPU (or Apple MPS), ready between calls. It keeps two rolling buffers — context audio and generated latents — shifted left by r·Tz each cycle to stay aligned with the sliding window without explicit sync with MAX. On each cycle it encodes context, applies Mcontext, runs the inpainting denoiser, decodes the new r·T slice, and streams it back.

RTAP Round-Trip — Trace One Prediction Cycle

Watch a chunk travel: MAX → (OSC/UDP) → server encode → sample → decode → (OSC/UDP) → MAX. Stage timings are the paper's config-2 (San Diego ↔ Paris) numbers. Toggle CD to watch the sampling stage shrink and the whole cycle drop under the 1.5-s deadline with margin.

How does the C++ external physically realize the look-ahead depth w?

Chapter 9: Results — Quality vs. Latency

Two things get measured: generation quality across look-ahead regimes, and end-to-end latency on real hardware. The story is consistent across both: things are great in Retrospective and degrade with look-ahead — and CD trades a little quality for a lot of speed.

Quality — three metrics, three regimes

Evaluated on Slakh2100 against the StreamMusicGen baseline (comparison is indicative, not direct — different codec, sample rate, instrument set). Three metrics:

The headline pattern: In the Retrospective zone, the diffusion model approaches the ground-truth ceiling on COCOLA and Beat F1, and achieves low FAD — strong, near-offline quality. As look-ahead increases, all metrics degrade gracefully. Notably, in the Look-ahead zone the diffusion model exceeds the StreamMusicGen Online Decoder at r=0.125 (0.75 s) and r=0.25 (1.5 s), even while scoring on a harder four-stem subset. The CD model tracks the same curve, slightly lower, but still marginally beats the baseline at 1.5 s look-ahead.
Quality vs. Net Look-Ahead — Reconstructs Figure 6

X-axis = net look-ahead T·r·w (seconds), split into Retrospective (green), Immediate (gray), Look-ahead (salmon) zones. Toggle metric. Watch diffusion (orange) and CD (teal) peak in Retrospective and decline as the model is forced to generate further into the unseen future.

Latency — does it actually run in real time?

Each cycle has five stages: MAX→server transfer, encode, sample, decode, server→MAX transfer. At r=0.25 the deadline is T·r = 1500 ms. The dominant term is sampling, and that's exactly what CD shrinks:

ConfigModelSamplingFull cycleRT (<1500ms)?
(2) Paris A6000Diffusion480 ms (10 passes)981 ms
(2) Paris A6000CD88 ms (2 passes)589 ms
(1) Local RTX 2070Diffusion1175 ms1398 ms
(1) Local RTX 2070CD130 ms362 ms

At r=0.25 all four deployments meet the deadline. But push to r=0.125 (750 ms budget) and the diffusion model fails (889 ms > 750 ms) while CD still passes (497 ms). That gap — diffusion failing where CD survives — is the entire justification for distillation.

The minimum feasible step (worked)

For local deployment, total cycle time decomposes as d(r) = dcompute + c·r (fixed compute + transfer scaling with chunk size). The real-time constraint d(r) < T·r rearranges to the minimum feasible step ratio:

r* = dcompute / (T − c)

Plugging the paper's best-case A6000 numbers (dcompute≈596 ms for diffusion, 204 ms for CD, c≈80 ms, T=6 s): r*≈0.100 for diffusion vs. r*≈0.034 for CD. The distilled model can theoretically run at roughly 3× finer step sizes — tighter timing, more context, better quality. (Though such fine steps weren't in training, so quality there is unverified — honest limitation flagged by the authors.)

The honest takeaway: Both models — and the baseline — still struggle in the Look-ahead regime. FAD degrades more sharply for the diffusion models than the baseline outside Retrospective. The paper doesn't claim the problem is solved; it claims feasibility and a principled framework, and concludes that high-quality look-ahead generation "largely remains an open challenge across paradigms."
At step size r = 0.125 (750 ms budget), what happens to the two models on the A6000?

Chapter 10: Connections & Cheat Sheet

This paper sits at the intersection of three lineages: generative audio modeling, latency-compensation control, and live computer-music systems. Its real novelty is treating inference latency as a first-class musical constraint and engineering around it.

Control theory
Smith predictor / model predictive control — forecast over a horizon, schedule commands ahead. The same "generate before you need it" idea.
Robot learning
Action chunking — emit a short sequence of future actions per inference, buffering execution while the next batch computes.
This paper (2026)
Look-ahead sliding-window LDM + consistency distillation + MAX/MSP RTAP — latency hiding for real-time music.
Lineage
StreamMusicGen (baseline), StemGen, Diff-A-Riff, MT-MusicLDM/MSG-LD (authors' prior work), Music2Latent (the frozen codec).

The cheat sheet — every key equation

IdeaEquation / RuleWhat each symbol means
Accompaniment tasks = f(xcontext(s))predict stem s from sum of other stems
Sliding windows,t = f(xcontext,t | SrT(x̂s,t−1))r = step fraction; only last r·T inpainted
Current-time position(w+1)·T·r from right edgew = look-ahead depth (−1/0/+1)
Context maskMctx[t]=1 iff t < Tz−Tz·r·(w+1)zeroes the unknown future frames
Target maskMs[t]=1 iff t < Tz−Tz·ronly last step's slice is open
DSM loss‖zs − gφ(zs+σε,·)‖2learn the score / denoiser
CD total lossLCD + λDSMLDSM, λ=0.7match EMA target + data grounding
Min feasible stepr* = dcompute/(T−c)real-time floor; CD lowers dcompute

Why this work matters

The whiteboard summary

A latent diffusion model generates one instrument stem conditioned on a mixture of the others, working entirely in Music2Latent's frozen 64×64 latent space. To run live despite ~1-second inference, it generates ahead of playback: a sliding window advances by T·r each step, and a look-ahead depth w places the predicted slice w steps into the future, hiding inference behind a playback buffer. The unknown future is realized by zeroing latent frames with an asymmetric context/target mask, randomized over r, w during training so one model serves all regimes. Consistency distillation collapses 10 denoising passes to 2 (5.4× faster), letting finer steps meet the deadline. A C++ MAX/MSP external streams chunks over OSC/UDP to a Python server and writes predictions w steps ahead in a shared buffer. Quality peaks in Retrospective, degrades gracefully with look-ahead, and beats the StreamMusicGen baseline at practical look-ahead depths — while confirming that high-quality look-ahead generation remains open.

"...similar to how human musicians anticipate, a model must generate ahead of playback, building up enough buffer that can be consumed while the next prediction is computed."
— Karchkhadze & Dubnov (2026)