Open JTalk の音素継続長の不具合を回避する

NVDA日本語化プロジェクト(nvdajp)は、オープンソース日本語音声合成エンジン Open JTalk公式サイト)に基づく音声エンジン JTalk を本家 NVDA に追加しています。
このエンジンで、読み上げるメッセージが長いときに、メッセージの後半で「極端に遅い音声」あるいは「不自然な音声」が生成されるという問題がありました。これは最新の HTS engine API 1.04 および Open JTalk 1.02 と m001 1.02 でも発生します。
もしかしたら MMDAgent や、その他のシステムの一部としてこの音声エンジンをお使いの方も、同じような現象にお気づきかも知れません。
この問題について、完全に解明できたわけではありませんが、このパッチ(github)にて回避できたように見えますので、ご報告します。
なお、この記事は私が libopenjtalk.dll の開発のために、独自に調査した内容です。もし誤り等がありましたら御指摘いただければ幸いです。

問題が発生するテキストを選び、エンジン側に printf() を突っ込んでひたすら現象を眺めてみました。
その結果、不自然に見える音素継続長は、混合ガウス分布の木構造から「もっとも環境にマッチするノード」を選択する処理で、選択されるべきノードが選択されず、デフォルトとして最もグローバルなノードが選択されている場合に発生する、という可能性が示唆されました。
おそらく最もグローバルなノードは無音モデルを含む継続長の平均が保持されており、これを「無音ではないモデル」の継続長として使用することが不具合を起こしているのでしょう。
次に、同じフレーズが繰り返して「前半では正しく読み上げられて、後半では正しく読み上げられない」パターンを作り、解析をしました。
うまくノードが選ばれている「環境ラベル」とそうでないラベルの違いは、「呼気段落中のアクセント句の位置」や「発話全体のモーラ数」などであることがわかりました。
おそらく話者モデルを学習したときに想定されなかった長さのセンテンスが入力されたため、ミスマッチが起きていると思われます。
発話全体のモーラ数やアクセント句の位置は、自然な音声の生成に有効な情報と思われますが、システムの頑健性も重要です。そこで、暫定的な回避策として上記のようなパッチを作りました。
本当は、ノード選択の際に、適切な質問を適切な順序で行うことによって、この現象は回避できるような気もします。質問がどのように選ばれるのか、詳しく調べたいのですが。。
なお、この現象は MMDAgent の mei_normal モデルではいまのところ確認できておらず、話者モデルに依存する現象です。
本件について、今後 Open JTalk の開発チームと情報共有を進めつつ、より「正しい」解決方法を検討していきたいと思います。
追記:この記事で扱っている技術については「HMM音声合成システム(HTS)の開発」などが参考になると思います。