初めに
今回は、piper-plusから独立したG2P(Grapheme-to-Phoneme、文字から音素への変換)パッケージ「piper-plus-g2p」を紹介します。
piper-plus-g2pは、TTSエンジンを使わずに8言語(ja, en, zh, ko, es, fr, pt, sv)のテキストをIPA音素列に変換できるパッケージです。このうちTTSモデルが公開済みなのは6言語(ja, en, zh, es, fr, pt)ですが、G2P単体としては8言語すべてに対応しています。従来のpiper-plusではeSpeak-ng(GPLライセンス)に依存していましたが、piper-plus-g2pはeSpeak-ngを完全に排除し、MITライセンスで利用できます。PR #300(2026-04-01)で追加し、2026-04-07にPyPI(v0.2.0)およびcrates.io(v0.2.0)で公開しました。
設計方針として「IPA-first」を採用しており、phonemize() はBOS/EOS/PUAマーカーを含まない純粋なIPAトークン列を返します。Piper互換のphoneme_idsが必要な場合はオプションの PiperEncoder を使います。
Python(PyPI: v0.2.0)、npm(@piper-plus/g2p: v0.2.0)、Rust(crates.io: v0.2.0)、Goの4つのSDKを提供しており、4実装間でPUA互換テーブル(96エントリ)をCIで一致検証しています。
開発環境
- OS: Windows 11
- Python: 3.12
- パッケージマネージャー: uv
- piper-plus-g2p: 0.2.0
環境構築
全言語をインストールする場合
uv init piper-g2p-demo cd piper-g2p-demo uv add "piper-plus-g2p[all]>=0.2.0"
日本語のみの場合
uv add "piper-plus-g2p[ja]"
個別言語のextras
必要な言語だけをインストールすることで依存関係を最小限にできます。
| extra | 言語 | 主な依存 | 備考 |
|---|---|---|---|
[ja] |
日本語 | pyopenjtalk-plus | |
[en] |
英語 | CMU Dictionary + g2p-en | |
[zh] |
中国語 | ピンイン辞書 | |
[ko] |
韓国語 | g2pk2 (MeCab/eunjeon) | Windows環境ではMeCabのインストールに問題が発生する場合あり |
[es] |
スペイン語 | ルールベース | |
[fr] |
フランス語 | ルールベース | |
[pt] |
ポルトガル語 | ルールベース | |
[sv] |
スウェーデン語 | NST辞書 |
複数言語を組み合わせる場合は以下のように指定します。
uv add "piper-plus-g2p[ja,en,zh]"
基本的な使い方
言語ごとのPhonemizer取得
get_phonemizer() に言語コードを渡すと、対応するPhonemizerインスタンスが返されます。
from piper_plus_g2p import get_phonemizer # 日本語のPhonemizer ja_phonemizer = get_phonemizer("ja") # テキストをIPA音素列に変換 result = ja_phonemizer.phonemize("こんにちは") print(result)
['k', 'o', '[', 'N_n', 'n', 'i', 'ch', 'i', 'w', 'a']
各言語の出力例
from piper_plus_g2p import get_phonemizer # 日本語 ja = get_phonemizer("ja") print("ja:", ja.phonemize("こんにちは")) # 英語 en = get_phonemizer("en") print("en:", en.phonemize("Hello, world!")) # 中国語 zh = get_phonemizer("zh") print("zh:", zh.phonemize("你好世界"))
ja: ['k', 'o', '[', 'N_n', 'n', 'i', 'ch', 'i', 'w', 'a'] en: ['h', 'ə', 'l', 'ˈ', 'o', 'ʊ', ',', ' ', 'w', 'ˈ', 'ɜ', 'ː', 'l', 'd', '!'] zh: ['n', 'i', 'tone2', 'x', 'aʊ', 'tone3', 'ʂ', 'ɻ̩', 'tone4', 'tɕ', 'iɛ', 'tone4']
注意: 韓国語(ko)のG2Pはg2pk2を使用しており、内部でMeCab(eunjeon)を必要とします。Windows環境ではMeCabのインストールに問題が発生する場合があるため、上記の例からは除外しています。Linux/macOS環境であれば
get_phonemizer("ko")で利用可能です。
IPA-first設計のため、出力にはBOS(^)やEOS($)などのマーカーは含まれません。
日本語の韻律情報(ProsodyInfo)
日本語Phonemizerでは、音素列に加えて韻律情報(アクセント・イントネーション)も取得できます。
from piper_plus_g2p import get_phonemizer ja = get_phonemizer("ja") tokens, prosody = ja.phonemize_with_prosody("こんにちは") print("prosody:", prosody[0])
prosody: ProsodyInfo(a1=-4, a2=1, a3=5)
OpenJTalkのアクセント情報(a1, a2, a3)がそのまま取得でき、TTS学習時のアクセント制御に利用できます。
PiperEncoderでphoneme_idsに変換(オプション)
Piper互換のphoneme_idsが必要な場合は PiperEncoder を使います。
from piper_plus_g2p import get_phonemizer, PiperEncoder import json ja = get_phonemizer("ja") phonemes = ja.phonemize("こんにちは") # config.jsonからphoneme_id_mapを読み込み with open("config.json") as f: config = json.load(f) encoder = PiperEncoder(config["phoneme_id_map"]) phoneme_ids = encoder.encode(phonemes) print(phoneme_ids)
PiperEncoder はコンストラクタに phoneme_id_map(モデルの config.json に含まれる音素→IDのマッピング辞書)が必要です。BOS(ID: 1)とEOS(ID: 2)は自動付加されます。
多言語テキストの自動言語検出
MultilingualPhonemizer
日本語と英語が混在するテキストを処理する場合は、get_phonemizer() にハイフン区切りの複合言語コード(例: "ja-en-zh")を渡します。内部で MultilingualPhonemizer が自動的に作成され、Unicodeブロックに基づく言語検出で混在テキストを処理します。
from piper_plus_g2p import get_phonemizer # ハイフン区切りの複合言語コードを渡すとMultilingualPhonemizerが自動生成される phonemizer = get_phonemizer("ja-en") # 日本語・英語混在テキスト result = phonemizer.phonemize("Dockerコンテナを起動する") print(result)
言語検出はUnicodeブロックに基づいて自動判定されます。
| 文字種 | 判定言語 |
|---|---|
| カナ文字を含む | 日本語 (ja) |
| CJK文字(カナなし) | 中国語 (zh) |
| ハングル | 韓国語 (ko) |
| ラテン文字 | 英語 (en) |
カスタム辞書サポート
特定の単語の読みをカスタマイズする場合、CustomDictionary クラスを使います。apply_to_text() でテキスト内の単語を辞書の読みに置換し、その後にG2Pで音素変換する流れです。
from piper_plus_g2p import get_phonemizer from piper_plus_g2p.custom_dict import CustomDictionary # カスタム辞書を作成し、単語を追加 d = CustomDictionary() d.add_word("Docker", "ドッカー", priority=10) d.add_word("piper", "パイパー", priority=10) # テキストに辞書を適用してから音素変換 text = "Dockerコンテナを起動する" text = d.apply_to_text(text) # "ドッカーコンテナを起動する" ja = get_phonemizer("ja") result = ja.phonemize(text) print(result)
JSON辞書ファイルからの読み込みにも対応しています。
d = CustomDictionary(dict_paths="my_dict.json")
他のSDKについて
piper-plus-g2pはPython以外にも複数のSDKを提供しています。
npm (@piper-plus/g2p: v0.2.0)
npm install @piper-plus/g2p
import { G2P, DictLoader } from "@piper-plus/g2p"; // 日本語にはOpenJTalk辞書の読み込みが必要 const loader = new DictLoader(); const jaDict = await loader.loadJaDict(); const g2p = await G2P.create({ languages: ["ja", "en"], jaDict }); const result = g2p.phonemize("こんにちは"); console.log(result.tokens); // ['k', 'o', ...] console.log(result.language); // 'ja' g2p.dispose();
G2P.create() は非同期ファクトリで、日本語はOpenJTalk WASM + 辞書データの初期化が必要です。DictLoader が辞書のダウンロードとIndexedDBキャッシュを管理します。phonemize() は { tokens, prosody, language } を返します。韻律情報が必要な場合は phonemizeWithProsody() を使います。380テストでカバーしています。
Rust (crates.io: v0.2.0)
cargo add piper-plus-g2p@0.2.0 --features naist-jdic
use piper_plus_g2p::Phonemizer; use piper_plus_g2p::japanese::JapanesePhonemizer; fn main() -> Result<(), Box<dyn std::error::Error>> { let ja = JapanesePhonemizer::new_bundled()?; let (tokens, prosody) = ja.phonemize_with_prosody("こんにちは")?; println!("{:?}", tokens); Ok(()) }
PhonemizerRegistry を使って複数言語をまとめて管理することもできます。Feature flagsで言語ごとの依存を制御可能です(features = ["naist-jdic", "english"])。390テストでカバーしています。
Go
Go SDKのG2Pはモノレポ内のモジュールとして提供しています。
import "github.com/ayutaz/piper-plus/src/go/phonemize" // 各言語のPhonemizerを個別に作成 esPhonemizer := phonemize.NewSpanishPhonemizer() result, err := esPhonemizer.PhonemizeWithProsody("Hola, mundo")
日本語G2PはCGO + OpenJTalkが必要です。詳細はGo APIサーバーの記事を参照してください。
4実装間の互換性保証
Python、npm、Rust、Goの4実装は、PUA互換テーブル(96エントリ)をCIで一致検証しています。どのSDKを使っても同じテキストに対して同じ音素列が生成されます。
所感
piper-plus-g2pはTTSを使わずに音素変換だけを利用したいユースケース(発音辞書生成、語学アプリ、音声検索のインデックス構築など)に適しています。eSpeak-ng(GPL)を排除してMITライセンスで8言語のG2Pが使える点は、商用プロダクトへの組み込みにも有利です。4つのSDKでCIレベルの互換性が保証されている点も実用的です。