Skip to content

fix(omnivoice): pass duration to model.generate instead of truncating#2011

Open
Arturogpj wants to merge 1 commit into
deepbeepmeep:mainfrom
Arturogpj:omnivoice-duration-fix
Open

fix(omnivoice): pass duration to model.generate instead of truncating#2011
Arturogpj wants to merge 1 commit into
deepbeepmeep:mainfrom
Arturogpj:omnivoice-duration-fix

Conversation

@Arturogpj

@Arturogpj Arturogpj commented Jul 15, 2026

Copy link
Copy Markdown

Problem

When a user lowers the duration slider for OmniVoice, WanGP returns a slice of the end of a naturally-paced generation instead of compressing the speech to fit the requested length.

Root cause

OmniVoice's model.generate(duration=X) is the upstream-supported way to fix output length (see k2-fsa/OmniVoice — "duration: fixed output duration in seconds. Overrides speed when set."). It sets target_lens = X * frame_rate and computes a speed ratio so the model fits the text into that duration.

WanGP's OmniVoicePipeline never passed duration= to model.generate() and instead generated at the model's natural pace and truncated the tail afterwards (pipeline.py _generate_segments):

audios = self.model.generate(text=..., generation_config=...)  # no duration=
...
segment_audio = segment_audio[:samples_left]  # chop off whatever didn't fit

So lowering the duration just cut off whatever the model hadn't had time to produce — not compression.

Fix

In app/models/TTS/omnivoice/pipeline.py:

  1. _run_segment now accepts a segment_duration param and passes it to self.model.generate(duration=segment_duration). The model now fits each segment's text into its allotted seconds — true compression via the upstream API.

  2. _generate_segments now distributes the requested duration across segments proportionally to their estimated speech seconds (using the existing _estimate_text_seconds helper). Inter-segment pauses count toward the request, so the final file length ≈ the requested duration. The old post-hoc truncation is replaced with a small safety trim (a few hundred ms) that only guards against post-processing silence overshoot — never a content cut.

  3. Auto mode (duration_seconds=0/None) is unchanged — segment_duration=None is passed to every segment, preserving the previous behavior.

Verification

Tested locally via the Wan2GP UI:

  • Same text at duration=15s → full-length natural speech (unchanged).
  • Same text at duration=5sfull text compressed (faster speech), not a 5s slice of the 15s output. ✅
  • duration=0 (auto) → unchanged behavior. ✅

Scope

Single file, ~36 lines added, 2 removed. No changes to modeling_omnivoice.py, the handler, or any other TTS pipeline.

OmniVoice's model.generate(duration=X) is the upstream-supported way
(k2-fsa/OmniVoice) to fix output length: it sets target_lens = X * frame_rate
and computes a speed ratio so the model fits the text into that duration.

The WanGP pipeline never passed duration= to model.generate(), and instead
generated at the model's natural pace and truncated the tail afterwards.
Lowering the duration slider therefore returned the end of a naturally-paced
generation instead of compressed speech.

Fix: give each segment a proportional duration budget (total minus pauses,
distributed by each segment's estimated speech seconds) and pass it to
model.generate(duration=...). Pauses count toward the request so the final
file length matches the request. A small safety trim handles post-processing
silence overshoot only — it is never a content cut.

Auto mode (duration_seconds=0/None) is unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant