tts: seed the vocoder left context with the ICL reference tail

The upstream pipeline decodes reference plus generated codes then trims,
giving the first generated frames causal context from the reference; the
generated only decode started the vocoder from an empty state and
colored the clone onset. Seed both decode paths with the last
min(ref_T, left_ctx_frames) reference frames: the streaming decoder
takes them below its emit cursor so they are never emitted, the buffered
path prepends them and strips their samples. Raising
codec_left_context_sec past the reference duration reproduces the
upstream full reference decode exactly.
This commit is contained in:
Pascal
2026-07-04 22:47:41 +02:00
parent 3e3b6c1712
commit 4057d0331f
2 changed files with 51 additions and 7 deletions
+20
View File
@@ -112,6 +112,26 @@ struct codec_chunked_decoder_stream {
by_k.assign((size_t) K, {});
}
// Seed the left context with the tail of the ICL reference codes so
// the first emitted chunk draws causal context from the reference
// instead of an empty decoder state, matching the upstream pipeline
// which decodes reference plus generated then trims. ref_kt is K
// major [K, ref_T]; the last min(ref_T, left_ctx_frames) frames are
// kept. Call once, after init and before any push_frame; the seeded
// frames sit below emit_start_frame so they are never emitted.
void seed_reference(const int32_t * ref_kt, int ref_T) {
int seed = ref_T < left_ctx_frames ? ref_T : left_ctx_frames;
if (seed <= 0) {
return;
}
for (int k = 0; k < K; k++) {
const int32_t * row = ref_kt + (size_t) k * (size_t) ref_T + (size_t) (ref_T - seed);
by_k[(size_t) k].insert(by_k[(size_t) k].end(), row, row + seed);
}
T_so_far = seed;
emit_start_frame = seed;
}
// Append one frame (K int32 codes, one per codebook). Drain any
// chunks that became emittable. Returns false on decode failure or
// when cb returns false (cancellation).