Skip to content

2pass: keep the phrase line, and label the gaps - #7

Merged
KakaruHayate merged 1 commit into
mainfrom
fix/2pass-phrase-line-and-gaps
Oct 4, 2026
Merged

KakaruHayate merged 1 commit into
mainfrom
fix/2pass-phrase-line-and-gaps

Conversation

@KakaruHayate

Copy link
Copy Markdown
Owner

Fixes the three problems reported in the 2PASS chain.

1. The phrase line was lost

breathe --merge wrote only phones + breath, so the first pass's texts/words
were gone before the second pass even ran — and the second pass reads only the
phones tier, so nothing could rebuild them. Two changes:

  • the merge now carries texts/words through into the merged TextGrid;
  • align --textgrid re-attaches that grouping to the new spans. Each token is
    mapped back to the text/word interval that contained it (by time, in the input
    TextGrid), and the tier builder aggregates by exactly that owner id — so the
    tiers come out with the original words and the new boundaries.

The mapping has to skip the empty intervals the writer pads every tier with, and
has to skip the stop symbols the aligner drops; when the shapes still disagree it
declines to guess and leaves the tiers as before.

2. Stretches no token covered stayed empty

TextGridTier gains a per-tier gap_label. align and breathe --merge fill
the phones tier with it — --fill-gaps LABEL, default SP, empty string to
keep the upstream "unlabelled" behaviour. texts/words are untouched (they
still pad with empty labels, which is what upstream's textgrid package does).

3. The mechanism, confirmed before designing the fix

SP is a stop symbol: encode_phones() drops it, so an SP inserted before
the second pass cannot survive it. AP/EP/GS are global symbols and do
survive. Filling therefore belongs at serialisation, after alignment — it is a
labelling step, not a token pushed into the model. A gap that is silence is SP,
and SP costs the alignment nothing.

Evidence

hanser_0_0.wav, three steps:

① align        12 phones + 7 SP-filled gaps, tiers texts/words/phones
② breathe      4 tiers (texts, words, phones, breath)   <- was 2
③ align        14 tokens (12 phones + 2 AP) + 8 SP gaps, 0 unlabelled
               texts: 今 天 我 们 唱 歌  (re-timed to the 2PASS alignment)

Test suite 30/30 (a new TextGrid.GapLabelIsPerTierAndDefaultsToEmpty covers the
per-tier fill).

Note

The first pass also fills its gaps with SP now, since the flag defaults to SP for
both commands. That is what makes the final annotation gap-free; pass
--fill-gaps "" to keep the old behaviour.

Three problems in the align -> breathe --merge -> align --textgrid chain:

1. The phrase line was lost.  `breathe --merge` wrote only phones + breath,
   discarding texts/words, and the second pass reads only phones -- so a
   re-alignment had nothing to rebuild the phrase from.  The merge now carries
   texts/words through, and the second pass re-attaches that grouping to the
   new spans: each token is mapped back to the text/word that contained it,
   which is exactly what build_alignment_tiers() aggregates by, so the tiers
   come out with the original words and the *new* boundaries.

2. Stretches no token covered stayed empty.  A Praat tier has to run
   continuously, and a dataset annotation should not leave a stretch without a
   label, so TextGridTier gains a per-tier `gap_label` and align/breathe fill
   the phones tier with it -- `--fill-gaps LABEL`, default SP, empty to keep
   the upstream "unlabelled" behaviour.  texts/words are unaffected.

3. Confirmed the mechanism before designing it: SP is a stop symbol and is
   dropped by encode_phones(), so an SP inserted before the second pass cannot
   survive it; AP/EP/GS are global symbols and do.  Filling therefore happens
   at serialisation, after alignment, and is a labelling step -- not a token
   inserted into the model.  A gap that is silence is SP, and SP costs the
   alignment nothing.

Verified on hanser_0_0: the first pass writes 12 phones + 7 SP-filled gaps, the
merge keeps all four tiers, and the second pass returns the six words
(今天我们唱歌) re-timed to the new alignment with 14 tokens (12 phones + 2 AP)
and 8 SP-filled gaps -- no unlabelled interval anywhere.  Test suite 30/30.
@coderabbitai

coderabbitai Bot commented Oct 4, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 3384cd46-8830-493c-9ba5-47a14b8e3eea
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ghfind-review ghfind-review Bot added the review: top ghfind author score; see https://ghfind.com label Oct 4, 2026
@KakaruHayate
KakaruHayate merged commit 8f854b2 into main Oct 4, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review: top ghfind author score; see https://ghfind.com

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant