💬

LinuxでPiperTTSによる音声読み上げを設定する

に公開

はじめに

Linux 上での TTS として昔から festival が利用されており、最近だと espeak-ng が利用されているようですが、より高音質な PiperTTS を利用するように設定してみました。

以下、AI による Linux における代表的な TTS エンジンのまとめです。

世代 代表的なエンジン 仕組み 特徴
第1世代 Festival / Flite 録音した音素の繋ぎ合わせ 機械的だが、当時としては画期的
第2世代 espeak-ng フォルマント合成(計算で音を作る) 極めて軽量・高速。 ロボット声
第3世代 Piper / VITS 深層学習 (Neural/AI) 人間そっくり。 GPU/CPU負荷は高め

ただし PiperTTS は日本語には対応していません。私の目的は英語の読み上げです。

自分の環境は Manjaro Linux (Arch Linux ベース)です。Arch 系なら同じではないかと思います。

設定はドキュメントに従えば難しくないのですが、そのままではトラブルもあったので、その対処法を残しておくのが主目的です。

設定方法のドキュメント

基本的には archlinux - Speech dispatcher に沿って設定を行います。日本語版のページは こちら です。

speech-dispatcher というパッケージ、およびそれが提供するサービスが Linux での TTS の取りまとめ役です。これが実際の TTS を行う festival や PiperTTS などへ処理を振り分ける、まさにディスパッチャーの役割を果たします。

パッケージのインストール

まずどの TTS エンジンを使うにせよ、speech-dispatcher パッケージをインストールします。一応 espeak-ng もインストールしてました。PiperTTS を使うのに必要なのか分かりませんが、espeak-ng が PiperTTS のようなエンジンのフロントの役割をしてたりするという記述も見かけたので、念の為記載しておきます。

sudo pacman -S speech-dispatcher espeak-ng

自分は PiperTTS での英語読み上げを行いたいので、piper-voices-en-us をインストールします。

yay -S piper-voices-en-us

PiperTTS の動作テスト

speech-dispatcher の前に PiperTTS がちゃんと TTS してくれるかチェックします。

echo "Arch Linux is the best" | piper-tts -m "/usr/share/piper-voices/en/en_US/ryan/high/en_US-ryan-high.onnx" -f - | aplay

まずこの時点で注意点が1つあります。私が確認した時点では、英語版の説明に piper-tts -q -m という記載だったのですが -q を付けると til みたいな音がずっと再生されるだけになります。

なお日本語版の解説には spd-say コマンドでの動作確認が記載されていますが、これは speech-dispatcher を利用します。ここでは speech-dispatcher の前に PiperTTS 単体で動作確認するのが目的です。

aplay コマンドは ALSA に付属する単純な WAV データの再生コマンドです。自分の環境では pipewire を利用しているので、pw-play というコマンドも利用できます。

echo "Arch Linux is the best" | piper-tts -m "/usr/share/piper-voices/en/en_US/ryan/high/en_US-ryan-high.onnx" -f - | pw-play --rate 22050 --channels 1 --raw -

pw-play の場合はサンプリングレート、チャネル数、生データであることを指定する必要がありました。あと標準入力からデータを受け取るには末尾に - を付ける必要があることに注意です。なお aplay はサンプリングレートなどを指定しなくても大丈夫でしたが、もちろん明示的に指定することも可能です。

speech-dispatcher の設定

ユーザー単位の設定は以下で行えます。質問に答えていけばよかったはずですが・・・ちょっと何を指定したか忘れてしまいました・・・。

spd-conf -uc

ユーザー単位の設定は ~/.config/speech-dispatcher/ 以下に格納されます。以下のファイルとディレクトリがありました。

clients  desktop  modules  speechd.conf

ここでは speechd.conf の内容をコメント抜きで記載しておきます。インデントバラバラなのは実際にそうなってたからです。

  LogLevel  3
  LogDir  "default"  
  DefaultRate   0
  DefaultPitch   0
  DefaultPitchRange   0
  DefaultVolume 100  
  DefaultLanguage   en
  SymbolsPreproc "char"
  SymbolsPreprocFile "gender-neutral.dic"
  SymbolsPreprocFile "font-variants.dic"
  SymbolsPreprocFile "symbols.dic"
  SymbolsPreprocFile "emojis.dic"
  SymbolsPreprocFile "orca.dic"
  SymbolsPreprocFile "orca-chars.dic"
  AudioOutputMethod   pipewire
  AddModule "piper-tts-generic"        "sd_generic"   "piper-tts-generic.conf"
  DefaultModule   piper-tts-generic
  Include "clients/*.conf"

これ書いてて気づいたのですが、AudioOutputMethod: pulse になってました・・・。pulseaudio で出力しても pipewire が互換レイヤで処理してたのだろうと思います。pipewire に変更しました。

あと DefaultModule espeak-ng になっていました。どうやら AddModule しているのが piper-tts-generic だけなので、DefaultModule の指定は無視されていたようです。

speech-dispatcher 経由で piper-tts 実行する設定

上記の speechd.conf の設定で、Piper を利用するように設定したわけですが、実際に piper-tts コマンドを使ってどのように再生するのか設定を行う必要があります。

AddModule で "piper-tts-generic.conf" という設定ファイル名を指定しているので、~/.config/speech-dispatcher/modules/piper-tts-generic.conf を編集します。

GenericExecuteSynth "echo \"$DATA\" | sed -z 's/\\n/ /g' | piper-tts -m \"/usr/share/piper-voices/en/en_US/ryan/high/en_US-ryan-high.onnx\" -s 21 -f - | pw-play --rate 22050 --channels 1 --format s16 --volume 0.8 --raw -"

AddVoice "en-US" "MALE1"   "en_US-ryan-high"

ここでは pw-play を使っています。これは --volume 0.8 を指定したかったからです。最大ボリュームだとピーク音量で音が破裂音のように歪むように感じたためです。aplay だと音量指定できないんですよね。でも speechd.conf に DefaultVolume 指定があることに、この記事を書いている間に気づきました。。。

動作確認

それでは spd-say コマンドで、speech-dispatcher 経由で会話させてみましょう。

以下で登録されている音声が確認できます。

spd-say -L
                     NAME                 LANGUAGE                  VARIANT
          en_US-ryan-high                    en-US                    MALE1

以下で喋らせることができます。内容は何でもいいです。archlinux の解説にあったものをそのまま使いました。

spd-say "Arch Linux is the best"

遭遇した問題

archlinux - Speech dispatcher の説明では、piper-tts-generic.conf の設定例は以下のようになっていました。

GenericExecuteSynth "export XDATA=\'$DATA\'; echo \"$XDATA\" | sed -z 's/\\n/ /g' | piper-tts -q -m \"/usr/share/piper-voices/en/en_US/ryan/high/en_US-ryan-high.onnx\" -s 21 -f - | your-audio-player"

AddVoice "en-US" "MALE1"   "en_US-ryan-high"

自分は以下の問題に遭遇しました。

  1. "til, til, til..." みたいな音しか出ない
  2. 電子書籍を読み上げさせた時に固まったり、その後の読み上げ音が変になったりする

1 は既に記載した通り、-q オプションを外せば解決です。

2 は your-audio-player のところに mpv を使っていたために発生していました。❝ みたいな ASCII 文字じゃないものや、図など、バイナリ扱いになってる部分なのかうまく piper-tts が再生できなかった場合に、どうも mpv が固まってプロセスが終了せず、別の文を読み上げさせた時に混信してしまって音声が歪んでしまっていたようです。aplay や pw-play を使った場合はプロセスが終了するようで、発生しません。

なお電子書籍を読んだり TTS 再生させるのには Thorium Reader というのを利用しています。
一度この現象が発生すると、

systemctl --user restart speech-dispatcher

を行わないと spd-say コマンドでも音声が歪んだままになります。

そして speech-dispatcher を再起動すると、Thorium Reader の接続先とは別のプロセスIDになってしまうためなのか、Thorium Reader 自体も再起動しないと音が出なくて面倒なことになっていました。

再生コマンドについて

試行錯誤の過程で、一旦は以下のシンプルな設定にしたのですが、

GenericExecuteSynth "piper-tts -m \"/usr/share/piper-voices/en/en_US/ryan/high/en_US-ryan-high.onnx\" -s 21 -f - <<< \"$DATA\" | pw-play --rate 22050 --channels 1 --format s16 --volume 0.8 --raw -"

公式の説明にある export XDATA=\'$DATA\'; echo \"$XDATA\" | sed -z 's/\\n/ /g' はやっても問題なく再生されたので入れることにしました。

恐らくですが、最初に環境変数に代入してから echo しているのは、シングルクォート内では特殊文字が機能しないので、まず特殊文字をただの文字として変数に格納して、シェルに解釈されないようにする安全のためなのかと思います。

sed -z 's/\\n/ /g' による改行コードの空白への置き換えは、やっておくと改行された文がうまく繋がって再生されるといったことがあるかもしれません。

まとめ

Arch Linux で Piper を使った TTS を設定してみました。
archlinux - Speech dispatcher の設定通りではうまくいかなかったり、音声再生に mpv を使って電子書籍の読み上げで固まったり音が変になったりといった現象に悩まされましたが、無事解決することができました。

Discussion