Skip to content

[Supertonic-3 / LiteRT] Greedy text chunking creates awkward intra-sentence pauses and isolates trailing single words #140

Description

@ksrvpvl

Environment

  • Model: Supertonic-3-LiteRT
  • Component: speech_core::SupertonicTokenizer / LiteRTSupertonicTts
  • Target Platform: Android (LiteRT / C++)

Description

When synthesizing text using LiteRTSupertonicTts, sentences that slightly exceed the per-chunk token limit (cap / max_codepoints, typically ~50–60 codepoints) suffer from unnatural pauses mid-sentence, frequently isolating the final 1 or 2 words into a standalone synthesis chunk.

This makes the voice sound unnatural, breaking phrases right before the last word (e.g., "... sous les [pause] arbres." or "... dans le [pause] jardin.").


Root Cause Analysis

After inspecting supertonic_tokenizer.cpp, the issue appears to stem from the interaction of three mechanisms:

  1. Greedy Chunk Packing in SupertonicTokenizer::chunk:
    When an oversize sentence is split on word boundaries, words are greedily accumulated until the capacity (cap) is reached:

    for (char32_t c : sent) {
        if (is_ws(c)) { push_word(); continue; }
        word.push_back(c);
        if (static_cast<int>(word.size()) >= cap) push_word();
    }

    For a typical sentence of ~58–62 codepoints and a cap of ~50, the first chunk consumes ~50 codepoints, leaving a tiny remnant of 7–9 codepoints (almost always exactly the final word) in the second chunk.

  2. Forced Terminal Period in SupertonicTokenizer::preprocess:

    if (!ends_with_terminal(out)) out.push_back('.');

    When a sentence is severed mid-phrase (e.g. "... dans le"), preprocess() appends a terminal period .. This forces the non-autoregressive Flow-Matching model to synthesize the fragment with falling terminal intonation, followed by generating "jardin." as a completely new sentence with independent acoustic boundaries.

  3. Sub-sentence Punctuation Ignored in kTerm:
    kTerm currently only splits at U".!?…。!?". Clauses separated by commas ,, colons :, or semicolons ; are not treated as valid chunk boundaries, causing long multi-clause sentences to bypass clause-level splitting and fall straight into the greedy word-packer.


Steps to Reproduce

Synthesize the following French paragraph with LiteRTSupertonicTts:

Bien sûr ! Voici une petite histoire de vacances d'été en 10 phrases :
Cet été, j'ai passé mes vacances à la campagne. J'ai loué une petite maison près d'un lac magnifique. Chaque matin, je me levais tôt pour faire une longue promenade. J'ai beaucoup nagé dans l'eau fraîche et claire. J'ai aussi passé du temps à lire des livres sous les arbres. Un après-midi, j'ai aidé mes voisins à jardiner. Nous avons mangé des fruits frais cueillis dans le jardin. Le soir, je regardais les étoiles depuis ma terrasse. C'était un été très calme et reposant. Je suis revenu à la ville avec beaucoup de souvenirs heureux.
Est-ce que tu veux que je traduise ce texte ou que je te raconte une autre histoire ?

Observed Behavior

Noticeable pauses and intonation breaks occur at:

  • "... de vacances d'été en 10" [pause] "phrases :" (~50 chars + 9 chars)
  • "... à lire des livres sous les" [pause] "arbres." (~52 chars + 7 chars)
  • "... cueillis dans le" [pause] "jardin." (~50 chars + 7 chars)
  • "... beaucoup de souvenirs" [pause] "heureux." (~52 chars + 8 chars)
  • "... ce texte ou que je te" [pause] "raconte une autre histoire ?" (~56 chars + 28 chars)

Sentences under ~48 codepoints (e.g., "Cet été, j'ai passé mes vacances à la campagne.") synthesize cleanly without any pauses.


Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions