Skip to content

Rust: 10 文字以下のテキストで SSML マークアップを読み上げてしまう (Strategy C のラッパが未パース) #694

Description

@ayutaz

概要

Rust ランタイムは 10 文字以下のテキストで SSML マークアップを音声として読み上げる。synthesize_with_params が Strategy C のラッパ文字列を SSML としてパースせず、そのまま phonemizer に渡しているため。

根拠(コード)

src/rust/piper-core/src/voice.rs:421-424:

// Strategy C: 短テキストを SSML <break> でラップ
let effective_text = crate::short_text::wrap_short_text_ssml(text);
let text_ref = effective_text.as_str();

// 1. Phonemize: テキストをトークン列 + プロソディ情報に変換
let (tokens, prosody) = self.phonemizer.phonemize_with_prosody(text_ref)?;

wrap_short_text_ssml(src/rust/piper-core/src/short_text.rs)は

<speak><break time="300ms"/>{text}<break time="300ms"/></speak>

を返すが、phonemize_with_prosody は SSML を解釈しない(piper-plus-g2p::ssml は存在するが、この経路からは呼ばれない)。

実測

--timing json の音素列(issue #656 の対応で音素名が見えるようになって発覚):

$ piper-plus-cli --model multilingual-test-medium.onnx --text "Sol" --timing json
^ _ s _ p _ ˈ _ i _ ː _ k _   _ b _ ɹ _ ˈ _ e _ ɪ _ k _   _ t _ ˈ _ a _ ɪ _ m _ ...
      └─ "speak" ─┘       └─── "break" ───┘      └── "time" ──┘

s p ˈ i ː k = speak、b ɹ ˈ e ɪ k = break、t ˈ a ɪ m = time。つまり <speak> <break time= というマークアップが英語として音素化されている。

phoneme ID 数の比較(phonemize_to_ids(text) vs 実際に推論へ渡った列):

テキスト 文字数 text 由来 ID 実際の ID 差
Sol 3 11 111 +100
Hola 4 15 115 +100
Hola mundo 10 31 131 +100
Hola mundo bonito hoy 21 — 一致 0

差 +100 がラッパ文字列の音素数にあたる。閾値 SHORT_TEXT_CHARS = 10 を超えると発生しない。

長文では正しく h ˈ o ʊ l ə("Hola")から始まり "speak" は現れない。

影響

他ランタイムとの比較

C++ は Strategy C を CLI の --ssml フラグ経由でのみ適用し(src/cpp/main.cpp)、短文の自動ラップは行っていない。Python / Go / C# の該当箇所は未調査。

修正方針(要判断)

  1. ラップ後に SSML としてパースする — piper_plus_g2p::ssml で segment 分解し、<break> を無音として扱う(C++ の synthesizeSsmlToBuffer と同じ形)
  2. ラップをやめ、無音を直接挿入する — SSML を経由せず、前後に SILENCE_PAD_MS 相当の無音サンプルを付ける。Strategy C の目的(短文の音質改善)はこれで達成できる
  3. Strategy C 自体を Rust では無効化する

2 が最も単純で、SSML パーサへの依存も増えない。

受け入れ条件

  • 10 文字以下のテキストで音素列にマークアップ由来の音素が現れない
  • Strategy C の無音パディングが(何らかの形で)維持されるか、無効化の判断が記録される
  • 閾値の前後(10 文字 / 11 文字)を通す回帰テストがある
  • Python / Go / C# の同型問題を調査する

関連

#656(Rust / Go の timing が音素名でなくプレースホルダ)の対応中に発見。プレースホルダを実音素名に変えた結果、初めて観測可能になった。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions