日本語対応の軽量TTSライブラリ「piper-plus」をPythonで動かす

初めに

先日以下のようなライブラリを発表しました

これを実際に動かしてみます (日本語の発音が怪しいのは、バグ + 学習不足なので後日改善される予定です)

以下がpiper-plusのリポジトリです

github.com

開発環境

環境構築

まずはuvで環境構築を行います

uv venv -p 3.12
.\.venv\Scripts\activate

次に必要なライブラリをインストールします

uv pip install piper-tts-plus==1.5.5 pyopenjtalk-plus

コードから推論を実行

以下のコードを実行して日本語と英語を生成します

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
piper-tts-plus 1.5.5 テストスクリプト
日本語と英語の音声合成
"""

import sys
import os
import wave
from pathlib import Path

# UTF-8エンコーディングを強制
os.environ['PYTHONIOENCODING'] = 'utf-8'
if sys.platform == "win32":
    sys.stdout.reconfigure(encoding='utf-8')
    sys.stderr.reconfigure(encoding='utf-8')

from piper import PiperVoice


def main():
    print("=== piper-tts-plus 1.5.5 音声合成テスト ===\n")

    # モデルパス
    base_path = Path(__file__).parent.parent
    ja_model = base_path / "test" / "models" / "ja_JP-test-medium.onnx"
    ja_config = base_path / "test" / "models" / "ja_JP-test-medium.onnx.json"
    en_model = base_path / "test" / "models" / "test_voice.onnx"
    en_config = base_path / "test" / "models" / "test_voice.onnx.json"

    # 日本語音声生成(prosody=Falseを強制)
    print("1. 日本語音声を生成中...")
    if ja_model.exists() and ja_config.exists():
        voice_ja = PiperVoice.load(str(ja_model), config_path=str(ja_config), use_cuda=False)

        # phonemizeメソッドをオーバーライドしてpyopenjtalk.g2p()を直接使用
        import pyopenjtalk
        from piper.phonemize.token_mapper import map_sequence

        original_phonemize = voice_ja.phonemize
        def custom_phonemize(text):
            """Use pyopenjtalk.g2p() with proper token mapping"""
            phoneme_str = pyopenjtalk.g2p(text)
            tokens = phoneme_str.split()
            # pauseを"_"に変換
            tokens = ["_" if t == "pau" else t for t in tokens]
            phonemes = ["^"] + tokens + ["$"]
            # 重要: map_sequenceでマルチ文字音素をPUA文字に変換
            return [map_sequence(phonemes)]

        voice_ja.phonemize = custom_phonemize

        japanese_text = "こんにちは、これはバージョン1.5.5のテストです"
        output_ja = "japanese_v155.wav"

        with wave.open(output_ja, "wb") as wav_file:
            voice_ja.synthesize(japanese_text, wav_file)

        # phonemizeメソッドを元に戻す
        voice_ja.phonemize = original_phonemize

        print(f"   ✓ 生成完了: {output_ja}")
        print(f"   テキスト: {japanese_text}")
    else:
        print("   ✗ 日本語モデルが見つかりません")

    # 英語音声生成
    print("\n2. 英語音声を生成中...")
    if en_model.exists() and en_config.exists():
        voice_en = PiperVoice.load(str(en_model), config_path=str(en_config), use_cuda=False)

        english_text = "Hello, this is a test with version 1.5.5"
        output_en = "english_v155.wav"

        with wave.open(output_en, "wb") as wav_file:
            voice_en.synthesize(english_text, wav_file)

        print(f"   ✓ 生成完了: {output_en}")
        print(f"   テキスト: {english_text}")
    else:
        print("   ✗ 英語モデルが見つかりません")

    print("\n=== 生成完了 ===")
    print("\n生成されたファイル:")
    for file in ["japanese_v155.wav", "english_v155.wav"]:
        if os.path.exists(file):
            size = os.path.getsize(file)
            print(f"  • {file} ({size:,} bytes)")

    if sys.platform == "win32":
        print("\n再生するには:")
        print("  start japanese_v155.wav")
        print("  start english_v155.wav")

    return 0


if __name__ == "__main__":
    try:
        sys.exit(main())
    except Exception as e:
        print(f"\nエラー: {e}")
        import traceback
        traceback.print_exc()
        sys.exit(1)

これを実行すると以下のようなログが表示されます

python test_v155.py
=== piper-tts-plus 1.5.5 音声合成テスト ===

1. 日本語音声を生成中...
   ✓ 生成完了: japanese_v155.wav
   テキスト: こんにちは、これはバージョン1.5.5のテストです

2. 英語音声を生成中...
eSpeak-NG not found
Missing phoneme from id map: H
Missing phoneme from id map: 1
Missing phoneme from id map: 5
Missing phoneme from id map: 5
   ✓ 生成完了: english_v155.wav
   テキスト: Hello, this is a test with version 1.5.5

=== 生成完了 ===

生成されたファイル:
  • japanese_v155.wav (177,708 bytes)
  • english_v155.wav (61,484 bytes)

再生するには:
  start japanese_v155.wav
  start english_v155.wav

これを実行することで以下のファイルが生成されます