Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5dcb338
docs: Misaki互換中国語G2P 設計ドキュメントとマイルストーン・チケット索引を追加
ayutaz Apr 12, 2026
dcf7976
docs: T01 PinyinToMisaki マッピングテーブル設計・実装 チケット追加
ayutaz Apr 12, 2026
0281300
docs: T02 PinyinToMisaki Convert メソッド統合 チケット追加
ayutaz Apr 12, 2026
4350448
docs: T03 ChineseG2PEngine ToMisakiIpa API 追加 チケット追加
ayutaz Apr 12, 2026
a8c9242
docs: T04 Misaki互換テスト実装 チケット追加
ayutaz Apr 12, 2026
49f4304
docs: T05 ドキュメント更新・品質保証 チケット追加
ayutaz Apr 12, 2026
911107a
docs: T06 Issue#56 フォローアップ・リリース準備 チケット追加
ayutaz Apr 12, 2026
bd61bac
docs: Phase 1 レビュー — T01/T02 の「一から作り直すとしたら」セクション強化
ayutaz Apr 12, 2026
502c4fa
docs: Phase 2 レビュー — T03/T04 の「一から作り直すとしたら」セクション強化
ayutaz Apr 12, 2026
a7030fe
docs: Phase 3 レビュー — T05/T06 の「一から作り直すとしたら」セクション強化
ayutaz Apr 12, 2026
6323a1f
feat(chinese): T01 PinyinToMisaki マッピングテーブル実装 (Mi1)
ayutaz Apr 12, 2026
3f9880c
feat(chinese): T02 PinyinToMisaki Convert メソッド実装 (Mi1)
ayutaz Apr 12, 2026
fdab28e
feat(chinese): Phase 1-R — PinyinToMisaki を Misaki 公式実装 (0.9.4) 準拠に全面…
ayutaz Apr 12, 2026
213176b
feat(chinese): Phase 2 — ToMisakiIPA API 追加 + 統合テスト 130 件 (Mi2)
ayutaz Apr 12, 2026
de607ce
chore: Phase 3 — v1.10.0 リリース準備 + ドキュメント更新 (Mi3, #56)
ayutaz Apr 12, 2026
f4dd75e
docs: M3 レビュー指摘修正 — チケット status・API名統一・CHANGELOG追加
ayutaz Apr 12, 2026
9757183
docs: PR #59 Copilot レビュー指摘修正 (8件)
ayutaz Apr 13, 2026
057764b
fix: PinyinToMisaki.cs.meta 欠落を修正 (CI Unity .meta チェック対応)
ayutaz Apr 13, 2026
9ea120b
fix: PR #59 レビュー指摘修正 — Y/W compound miss ガード + テスト修正
ayutaz Apr 13, 2026
0505ec6
fix: DocFX InvalidFileLink 警告修正 — tickets/ へのリンクをテキスト参照に変更
ayutaz Apr 13, 2026
c2fb320
fix: PR #59 レビュー指摘修正 — Y/W+Er ガード・Separatorテスト・出力例整合性
ayutaz Apr 13, 2026
e997b6b
Merge branch 'main' into feature/misaki-compatible-chinese
ayutaz Apr 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.10.0] - 2026-04-13

### Added
- **DotNetG2P.Chinese**: Misaki (Kokoro TTS) 互換 IPA 出力 API を追加 (#56)
- `ChineseG2PEngine.ToMisakiIPA(text)` — Misaki 互換 IPA 文字列(矢印声調記号付き)
- `ChineseG2PEngine.ToMisakiIPA(text, includeTones)` — 声調制御付き
- `ChineseG2PEngine.ToMisakiIPABatch(texts)` / `ToMisakiIPABatch(texts, includeTones)` — バッチ変換
- `PinyinToMisaki` 内部変換クラス — Misaki 0.9.4 実測ゴールドデータ (137件) で検証済み
- 統合テスト 130 件追加

### Changed
- (なし)

### Fixed
- (なし)

## [1.9.0] - 2026-04-02

### Added
Expand Down Expand Up @@ -303,7 +319,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Removed
- LibNMeCab依存を削除

[Unreleased]: https://github.com/ayutaz/dot-net-g2p/compare/v1.9.0...HEAD
[Unreleased]: https://github.com/ayutaz/dot-net-g2p/compare/v1.10.0...HEAD
[1.10.0]: https://github.com/ayutaz/dot-net-g2p/compare/v1.9.0...v1.10.0
[1.9.0]: https://github.com/ayutaz/dot-net-g2p/compare/v1.8.2...v1.9.0
[1.8.2]: https://github.com/ayutaz/dot-net-g2p/compare/v1.8.1...v1.8.2
[1.8.1]: https://github.com/ayutaz/dot-net-g2p/compare/v1.8.0...v1.8.1
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ OpenJTalk互換の日本語G2Pパイプライン、CMU辞書ベースの英語G2

## 進捗状況

全マイルストーン完了済み。現在 v1.9.0。
全マイルストーン完了済み。現在 v1.10.0。

| 言語 | パッケージ | 状態 | テスト数 | 備考 |
|------|-----------|------|---------|------|
| 日本語 | DotNetG2P.Core + MeCab | M1-M7完了 | 950+ | OpenJTalk互換パイプライン、独自MeCabエンジン、パフォーマンス最適化済み |
| 英語 | DotNetG2P.English | E1-E7完了 | 511 | CMU辞書135k語 + Flite LTS CARTツリー(PER 5.26%)、同綴異音語解決、piper-plus互換IPA |
| 中国語 | DotNetG2P.Chinese | C1-C6完了 | 936 | pinyin-data 44k + phrase-pinyin-data 412kエントリ、声調変調、IPA/注音/piper-plus互換 |
| 中国語 | DotNetG2P.Chinese | C1-C6 + Mi1-Mi3完了 | 1776+ | pinyin-data 44k + phrase-pinyin-data 412kエントリ、声調変調、IPA/注音/piper-plus互換、Misaki互換IPA出力(Kokoro TTS向け) |
| 韓国語 | DotNetG2P.Korean | K1-K4完了 | 375 | Hangul-first規則ベース、IPA/PUA/Prosody API |
| スペイン語 | DotNetG2P.Spanish | S1-S4完了 | 355 | ipa-dict PER 1.69%(base)/1.37%(allophones)、LatinAmerican/Castilian方言 |
| フランス語 | DotNetG2P.French | F1-F4完了 | 719 | 例外辞書500+語、Metropolitan/Conservative方言 |
Expand Down
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
<BaseOutputPath>$(MSBuildThisFileDirectory).build\bin\$(MSBuildProjectName)\</BaseOutputPath>

<IsPackable>false</IsPackable>
<Version Condition="'$(CI)' == 'true' Or '$(GITHUB_ACTIONS)' == 'true'">1.9.0</Version>
<Version Condition="'$(CI)' != 'true' And '$(GITHUB_ACTIONS)' != 'true'">1.9.0-local</Version>
<Version Condition="'$(CI)' == 'true' Or '$(GITHUB_ACTIONS)' == 'true'">1.10.0</Version>
<Version Condition="'$(CI)' != 'true' And '$(GITHUB_ACTIONS)' != 'true'">1.10.0-local</Version>
<PackageVersion>$(Version)</PackageVersion>
<Deterministic>true</Deterministic>
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true' Or '$(GITHUB_ACTIONS)' == 'true'">true</ContinuousIntegrationBuild>
Expand Down
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ enEngine.ToPhonemes("hello world"); // => "HH AH0 L OW1 W ER1 L D"
// 中国語G2P(ピンイン変換)
using var zhEngine = new ChineseG2PEngine();
zhEngine.ToPinyin("你好世界"); // => "ní hǎo shì jiè"
zhEngine.ToMisakiIPA("你好世界"); // => Misaki互換IPA(Kokoro TTS向け)

// 韓国語G2P
using var koEngine = new KoreanG2PEngine();
Expand Down Expand Up @@ -75,7 +76,7 @@ multiEngine.ToPhonemes("今日は안녕하세요 hello"); // 日本語部分は
- **Unity対応** — .NET Standard 2.1(Unity 2021.2+)ターゲット、UPMパッケージ提供
- **拡張可能な設計** — `ITokenizer`インターフェースにより形態素解析エンジンを差し替え可能
- **英語G2P対応** — CMU辞書(135,000語)+ Flite LTSルールによるOOV推定、IPA/X-SAMPA出力、テキスト正規化、同綴異音語解決、piper-plus 互換 IPA/PUA/Prosody API
- **中国語G2P対応** — pinyin-data単字辞書(44,000語)+ phrase-pinyin-dataフレーズ辞書(411,000語)による多音字自動解決、声調変調(三声連読・一/不変調)、3種の出力スタイル、IPA(国際音声記号)・注音符号(ボポモフォ)出力、piper-plus 互換 IPA/PUA/Prosody API
- **中国語G2P対応** — pinyin-data単字辞書(44,000語)+ phrase-pinyin-dataフレーズ辞書(411,000語)による多音字自動解決、声調変調(三声連読・一/不変調)、多彩な出力スタイル、IPA(国際音声記号)・注音符号(ボポモフォ)出力、Misaki互換IPA出力(Kokoro TTS向け)、piper-plus 互換 IPA/PUA/Prosody API
- **韓国語G2P対応** — Hangul-first の規則ベース変換、Jamo 分解、例外辞書、軽量正規化、`ㅎ` 系変化・終声中和・連音・濃音化・鼻音化・流音化を含む標準発音寄り rule engine、piper-plus 互換 IPA/PUA/Prosody API、benchmark harness、external corpus gate、performance test を実装
- **スペイン語G2P対応** — ルールベースIPA変換、音節分割、ストレス付与、Castilian/Latin American 切り替え、異音処理オプション、略語/数値/通貨/割合の正規化、例外辞書、全量コーパス評価ツールを実装。桁区切り/小数点の解釈分離と不正な日付/時刻の安全なフォールバックにも対応。PUA/Prosody API
- **フランス語G2P対応** — ルールベース6フェーズG2P変換(ダイグラフ→文脈依存→鼻母音化→半母音化→位置の法則→黙字)、音素ベース音節分割、Metropolitan/Conservative方言切り替え、異音処理(R無声化・阻害音有声性同化)、例外辞書500+エントリ(外来語/不規則語/動詞3複/学術語/同綴異音語)、テキスト正規化(数値/日付/時刻/通貨/単位/略語/記号)、IPA/X-SAMPA出力、全量コーパス評価ツール、PUA/Prosody API を実装
Expand Down Expand Up @@ -239,6 +240,18 @@ string[] zhPua = zhEngine.ToPuaPhonemes("你好世界");
// Prosody 情報
var zhResult = zhEngine.ToIpaWithProsody("你好世界");

// Misaki 互換 IPA(Kokoro TTS向け)
string misakiIpa = zhEngine.ToMisakiIPA("你好");
// => "ni↗ xau↓"(三声連読適用: 你3+好3 → 你2+好3)
// 声調記号: → (1声), ↗ (2声), ↓ (3声), ↘ (4声)

// 声調なし Misaki 互換 IPA
string misakiNoTone = zhEngine.ToMisakiIPA("你好", includeTones: false);
// => "ni xau"

// バッチ変換
var misakiBatch = zhEngine.ToMisakiIPABatch(new[] { "你好", "世界" });

// === 英語G2P ===
using DotNetG2P.English;

Expand Down Expand Up @@ -510,6 +523,10 @@ using var multiUnityEngine = new MultilingualG2PEngine(japaneseDicPath, unityOpt
| `ToPuaStringBatch(texts)` | `IReadOnlyList<string>` | バッチ PUA 文字列変換 |
| `ToIpaWithProsodyBatch(texts)` | `IReadOnlyList<ChineseProsodyResult>` | バッチ IPA+Prosody 変換 |
| `ToIpaWithProsodyBatch(texts, includeTones)` | `IReadOnlyList<ChineseProsodyResult>` | バッチ IPA+Prosody 変換(声調制御) |
| `ToMisakiIPA(text)` | `string` | Misaki互換IPA文字列(矢印声調記号付き) |
| `ToMisakiIPA(text, includeTones)` | `string` | 声調制御付きMisaki互換IPA |
| `ToMisakiIPABatch(texts)` | `IReadOnlyList<string>` | バッチMisaki互換IPA変換 |
| `ToMisakiIPABatch(texts, includeTones)` | `IReadOnlyList<string>` | バッチMisaki互換IPA変換(声調制御) |

### KoreanG2PEngine

Expand Down
201 changes: 201 additions & 0 deletions docs/guides/misaki-compatible-chinese.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
# Misaki互換 中国語G2P出力モード 設計ドキュメント

> 対応Issue: [#56 - How can i make result similar like misaki does?](https://github.com/ayutaz/dot-net-g2p/issues/56)

## 背景

[Kokoro TTS](https://github.com/hexgrad/kokoro) (82Mパラメータ) はG2Pフロントエンドとして [Misaki](https://github.com/hexgrad/misaki) を使用する。C#によるKokoro推論エンジン ([KokoroSharp](https://github.com/Lyrcaxis/KokoroSharp)) が存在するが、MisakiのC#ポートが無いため中国語G2P品質が低い (eSpeak-ng依存、KokoroSharp Issue#5)。

DotNetG2P.ChineseにMisaki互換出力モードを追加することで、C#/UnityのKokoro TTSエコシステムで採用可能になる。

## 現状の差異

`"你好"` の変換結果:

| 項目 | Misaki (Legacy) | DotNetG2P 現行 |
|------|----------------|----------------|
| 出力例 | `ni↓ xau↓` | `ni˧˥ xaʊ˨˩˦` |
| 声調記号 | 矢印 (`→` `↗` `↓` `↘`) | IPA tone letters (`˥˥` `˧˥` `˨˩˦` `˥˩`) |
| 音節区切り | スペース (音節間) | スペース (音節間) |
| 声母 j/q | `ʨ` / `ʨʰ` | `tɕ` / `tɕʰ` |
| 二重母音 | `ai` `au` `ei` `ou` (非音節化符号は出力に含まれない) | `aɪ` `aʊ` `eɪ` `oʊ` (別字母) |
| zh/ch/sh+i | `ɻ̩` / `ʐ̩` | `ɻ̩` |
| z/c/s+i | `ɹ̩` / `z̩` | `ɹ̩` |

### 声調マッピング詳細

| 声調 | DotNetG2P (Chao式) | Misaki (矢印) |
|------|-------------------|--------------|
| 1声 (陰平) | `˥˥` | `→` |
| 2声 (陽平) | `˧˥` | `↗` |
| 3声 (上声) | `˨˩˦` | `↓` |
| 4声 (去声) | `˥˩` | `↘` |
| 軽声 | なし | なし |

### 声母マッピング差異

| ピンイン | DotNetG2P | Misaki |
|---------|-----------|--------|
| j | `tɕ` | `ʨ` |
| q | `tɕʰ` | `ʨʰ` |
| x | `ɕ` | `ɕ` (同一) |

### 韻母 (二重母音) マッピング差異

| 韻母 | DotNetG2P | Misaki |
|------|-----------|--------|
| ai | `aɪ` (U+026A) | `ai` (非音節化符号は出力に含まれない) |
| ei | `eɪ` | `ei` |
| ao | `aʊ` (U+028A) | `au` |
| ou | `oʊ` | `ou` |
| uai | `uaɪ` | `uai` |
| ui | `ueɪ` | `uei` |
| iu | `ioʊ` | `iou` |

## 実装方式

### 方式比較

| 方式 | 概要 | 判定 |
|------|------|------|
| A: PinyinStyle に追加 | PinyinStyle はピンイン表記用 enum。IPA 出力とはレイヤーが異なる | **不採用** (責務混在) |
| **B: PinyinToMisaki.cs 新規 + ToMisakiIPA()** | PiperIpa と同パターン。独立マッピングテーブル | **採用** |
| C: ToIPA() 出力のポストプロセス | 文字列置換で変換。脆弱で将来変更に弱い | **不採用** |

### 採用: 方式B — 独立変換クラス + 専用メソッド

既存の `PinyinToIpa` / `PinyinToPiperIpa` / `PinyinToZhuyin` と同じ「変換先ごとに独立クラス」パターンに従う。

```
src/DotNetG2P.Chinese/Conversion/
├── PinyinToIpa.cs ← 標準IPA (既存)
├── PinyinToPiperIpa.cs ← piper-plus互換 (既存)
├── PinyinToZhuyin.cs ← 注音符号 (既存)
└── PinyinToMisaki.cs ← Misaki互換 (新規)
```

**選定理由:**

1. **一貫性**: 全変換クラスが独立マッピングテーブルを持つ既存設計に完全合致
2. **拡張性**: `RunPipeline` の `Func<string, string> converter` 委譲パターンにそのまま乗る
3. **保守性**: Misaki の仕様変更時にテーブル差分のみの修正で対応可能
4. **独立性**: 既存の ToIPA / ToPiperIPA 出力に一切影響しない

## 変更ファイル一覧

### 新規作成

| ファイル | 内容 |
|---------|------|
| `src/DotNetG2P.Chinese/Conversion/PinyinToMisaki.cs` | Misaki互換マッピングテーブル (声母/韻母/声調) |
| `tests/DotNetG2P.Tests/ChineseG2P/ChineseMisakiIpaTests.cs` | Misaki互換出力テスト |

### 変更

| ファイル | 内容 |
|---------|------|
| `src/DotNetG2P.Chinese/ChineseG2PEngine.cs` | `ToMisakiIPA()` / `ToMisakiIPABatch()` 公開メソッド追加 |

### 変更不要 (共通基盤)

- `PinyinParser.cs` — ピンイン解析 (共通)
- `ToneConverter.cs` — 声調変換 (共通)
- `ToneSandhiProcessor.cs` — 声調変調 (共通、結果はそのまま反映)
- `ChineseG2POptions.cs` — オプション (Separator等は既存で対応可能)
- `DotNetG2P.Multilingual/` — 初期対応では変更不要

## PinyinToMisaki.cs 設計

### 声母マッピング

```csharp
private static readonly Dictionary<Initial, string> s_initialIpa = new()
{
[Initial.B] = "p",
[Initial.P] = "pʰ",
[Initial.M] = "m",
[Initial.F] = "f",
[Initial.D] = "t",
[Initial.T] = "tʰ",
[Initial.N] = "n",
[Initial.L] = "l",
[Initial.G] = "k",
[Initial.K] = "kʰ",
[Initial.H] = "x",
[Initial.J] = "ʨ", // DotNetG2P: tɕ → Misaki: ʨ
[Initial.Q] = "ʨʰ", // DotNetG2P: tɕʰ → Misaki: ʨʰ
[Initial.X] = "ɕ",
[Initial.Zh] = "ʈʂ",
[Initial.Ch] = "ʈʂʰ",
[Initial.Sh] = "ʂ",
[Initial.R] = "ɻ",
[Initial.Z] = "ʦ", // DotNetG2P: ts → Misaki: ʦ
[Initial.C] = "ʦʰ", // DotNetG2P: tsʰ → Misaki: ʦʰ
[Initial.S] = "s",
[Initial.Y] = "j",
[Initial.W] = "w",
};
```

### 韻母マッピング (二重母音の差異)

```csharp
// 主な差異: ɪ→i, ʊ→u (非音節化符号 U+032F は出力に含まれない)
[Final.Ai] = "ai", // aɪ → ai
[Final.Ei] = "ei", // eɪ → ei
[Final.Ao] = "au", // aʊ → au
[Final.Ou] = "ou", // oʊ → ou
[Final.Iao] = "iau", // iaʊ → iau
[Final.Iu] = "iou", // ioʊ → iou
[Final.Uai] = "uai", // uaɪ → uai
[Final.Ui] = "uei", // ueɪ → uei
```

### 声調マッピング

```csharp
private static readonly string[] s_toneArrows = new[]
{
"", // Neutral (0) - なし
"→", // First (1)
"↗", // Second (2)
"↓", // Third (3)
"↘", // Fourth (4)
};
```

## ChineseG2PEngine 公開API

既存の `ToPiperIPA` パターンに準拠:

```csharp
// 文字列出力
public string ToMisakiIPA(string text)
public string ToMisakiIPA(string text, bool includeTones)

// バッチ出力
public IReadOnlyList<string> ToMisakiIPABatch(string[] texts)
public IReadOnlyList<string> ToMisakiIPABatch(string[] texts, bool includeTones)
```

## テスト方針

`ChineseMisakiIpaTests.cs` で以下をカバー:

1. **声調マッピング**: 各声調 (1-4 + 軽声) が正しい矢印記号に変換されること
2. **声母マッピング**: j/q → ʨ/ʨʰ、z/c → ʦ/ʦʰ 等の差異が反映されること
3. **韻母マッピング**: 二重母音の非音節化符号が正しいこと
4. **声調変調**: ToneSandhi の結果が Misaki 出力にも反映されること
5. **エッジケース**: 軽声、er化、句読点、空文字列等
6. **Misaki 出力例との比較**: issue #56 の `ni↓ xau↓` 等

## 備考

- Misaki には Legacy パス (IPA+矢印) と v1.1 パス (注音符号) の2つが存在するが、Kokoro-82M で使用されるのは Legacy パスのみ。本対応は Legacy パスを対象とする
- Misaki が `ꭧ` (U+AB67) を zh/ch の子音IPAに使用する件は、現行実装で ꭧ (U+AB67) / ꭧʰ を使用している
- Multilingual 層への統合は将来の追加作業とする

### Phase 1-R 実装知見

- Misaki legacy は 3-3 tone sandhi (三声連読変調) を適用しない。DotNetG2P では `EnableToneSandhi` オプションで制御可能。Misaki legacy と完全一致させるには `EnableToneSandhi = false` でエンジンを初期化する
- U+032F (COMBINING INVERTED BREVE BELOW, 非音節化符号) は Misaki テンプレート側で事前除去されるため、`ToMisakiIPA()` の出力には含まれない
Loading
Loading