feat: GPL-free 6言語マルチリンガル TTS — 学習パイプライン + C++ G2P - #218
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
バイリンガル(JA+EN)TTS のコードスイッチング対応を学習〜推論(ONNX)まで貫通させ、言語埋め込みやログ/最適化も含めてパイプラインを拡張するPRです。
Changes:
ja-enのバイリンガル音素化(統一 phoneme_id_map・Unicodeベースのセグメンテーション)を追加- 学習側に language_id を伝播し、
SynthesizerTrnに language embedding を追加(ONNX export / infer もlid対応) - WandB音声ログ最適化、WavLM の間引き実行、DataLoader/Optimizer 最適化などを追加
Reviewed changes
Copilot reviewed 16 out of 17 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| test/test_phonemizer_registry.py | get_phoneme_id_map() の monolingual/bilingual 挙動をテストで明確化 |
| test/test_bilingual_phonemizer.py | バイリンガルIDマップ、セグメンテーション、レジストリ統合のテストを追加 |
| src/python/piper_train/vits/models.py | 言語埋め込みの追加、global conditioning 統合、maximum_path 実装の切替 |
| src/python/piper_train/vits/lightning.py | language_id 伝播、固定テストセット、WandB音声ログ、最適化フラグ追加 |
| src/python/piper_train/vits/dataset.py | Dataset/Batch に language_id(s) を追加し collate でバッチ化 |
| src/python/piper_train/preprocess.py | ja-en 前処理(BILINGUAL phoneme type・ワーカー)を追加 |
| src/python/piper_train/phonemize/registry.py | ja-en phonemizer の自動登録を追加 |
| src/python/piper_train/phonemize/bilingual_id_map.py | JA+EN 統一 phoneme_id_map を新規追加 |
| src/python/piper_train/phonemize/bilingual.py | Unicodeベースの言語セグメント分割+委譲 phonemizer を新規追加 |
| src/python/piper_train/infer_onnx.py | ONNX 推論で lid 入力対応& --text モードの language_id 決定追加 |
| src/python/piper_train/export_onnx.py | ONNX export で lid 入力対応&ダミー入力構築を変更 |
| src/python/piper_train/main.py | num_languages 読み込み、WavLM無効化/間引き、--compile 追加 |
| pyproject.toml | 依存関係の追記、setuptools の packages.find 設定追加 |
| prepare_bilingual_dataset.py | 既存JA+ENデータを統合し学習用 dataset/config を生成するスクリプト追加 |
| benchmark_optimizations.sh | 最適化のベンチマーク用スクリプト追加 |
| CLAUDE.md | バイリンガル学習完了・使い方・最適化/ログ機能のドキュメント更新 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
ayutaz
force-pushed
the
feat/bilingual-phonemizer
branch
from
February 18, 2026 11:56
9e1e8db to
dd1cdfe
Compare
ayutaz
added a commit
that referenced
this pull request
Feb 18, 2026
- export_onnx.py: dummy_inputとinput_namesの条件分岐を一致させる(単一話者/言語モデルでNoneが渡される問題) - lightning.py: AdamW fused=TrueをCUDA有無で切り替え(CPU環境でTypeError) - bilingual_id_map.py: ENGLISH_PHONEMESの重複 "'" を削除 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ayutaz
added a commit
that referenced
this pull request
Mar 1, 2026
- 次のステップにCI修正完了を追記 - 実装済み機能にCI修正セクション追加 - 関連PR/IssueテーブルにPR #218追加 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ayutaz
added a commit
that referenced
this pull request
Mar 16, 2026
- export_onnx.py: dummy_inputとinput_namesの条件分岐を一致させる(単一話者/言語モデルでNoneが渡される問題) - lightning.py: AdamW fused=TrueをCUDA有無で切り替え(CPU環境でTypeError) - bilingual_id_map.py: ENGLISH_PHONEMESの重複 "'" を削除 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ayutaz
added a commit
that referenced
this pull request
Mar 16, 2026
- 次のステップにCI修正完了を追記 - 実装済み機能にCI修正セクション追加 - 関連PR/IssueテーブルにPR #218追加 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ayutaz
force-pushed
the
feat/bilingual-phonemizer
branch
from
March 16, 2026 02:52
9f25faf to
98f1053
Compare
This was
linked to
issues
Mar 16, 2026
Closed
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 58 out of 59 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
ayutaz
added a commit
that referenced
this pull request
Mar 16, 2026
- export_onnx: dummy_input/input_names条件を統一し入力数不一致を防止 - export_onnx: enc_pにg(conditioning)を渡しcond_layerをONNXに反映 - __main__: torch.compileをLightningModule全体→サブモジュール(model_g/d)に限定 - lightning: Utterance型をintで統一 (LongTensorではなく) - lightning: config.json読み込みをループ外に移動しI/O削減 - registry: _detect_default_latin()のフォールバックをlanguages内に限定 - multilingual: default_latin_language未サポート時のバリデーション追加 - bilingual_id_map: ENGLISH_PHONEMESの"?"重複を削除 - infer_onnx: _cache型アノテーションをtuple[tuple[str,int],...]に修正 - multi-gpu-training.md: 実装にないstatic_graph記述を削除
ayutaz
added a commit
that referenced
this pull request
Mar 16, 2026
- export_onnx.py: dummy_inputとinput_namesの条件分岐を一致させる(単一話者/言語モデルでNoneが渡される問題) - lightning.py: AdamW fused=TrueをCUDA有無で切り替え(CPU環境でTypeError) - bilingual_id_map.py: ENGLISH_PHONEMESの重複 "'" を削除 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ayutaz
added a commit
that referenced
this pull request
Mar 16, 2026
- 次のステップにCI修正完了を追記 - 実装済み機能にCI修正セクション追加 - 関連PR/IssueテーブルにPR #218追加 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ayutaz
added a commit
that referenced
this pull request
Mar 16, 2026
- export_onnx: dummy_input/input_names条件を統一し入力数不一致を防止 - export_onnx: enc_pにg(conditioning)を渡しcond_layerをONNXに反映 - __main__: torch.compileをLightningModule全体→サブモジュール(model_g/d)に限定 - lightning: Utterance型をintで統一 (LongTensorではなく) - lightning: config.json読み込みをループ外に移動しI/O削減 - registry: _detect_default_latin()のフォールバックをlanguages内に限定 - multilingual: default_latin_language未サポート時のバリデーション追加 - bilingual_id_map: ENGLISH_PHONEMESの"?"重複を削除 - infer_onnx: _cache型アノテーションをtuple[tuple[str,int],...]に修正 - multi-gpu-training.md: 実装にないstatic_graph記述を削除
ayutaz
force-pushed
the
feat/bilingual-phonemizer
branch
from
March 16, 2026 14:10
5a7754e to
65639b5
Compare
統一phoneme_id_map (JA+EN ~110記号) とUnicode範囲ベースの言語自動検出により、 「今日はgood morningですね」のような混合テキストを正しく音素化できる。 Phase B (学習パイプライン) / Phase C (データ準備+学習) の基盤。 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- SynthesizerTrnにnn.Embedding(n_languages, gin_channels)追加 - Dataset/Batchにlanguage_id伝播 - ONNX export/推論にlid入力対応 - preprocess.pyに--language ja-enバイリンガルモード追加 - config.jsonにnum_languages/language_id_map追加 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- validation_stepにlid (language_id)を追加し、EN utteranceの評価精度を改善 - infer_onnx.py --textモードでconfig.jsonのlanguage_id_mapからlanguage_idを決定 - dataset.py collateでlanguage_idsテンソルを.zero_()で初期化(ゴミ値防止) - bilingual_id_mapを実際のEnglishPhonemizer出力に合わせて101→97シンボルに修正 - CLAUDE.mdをバイリンガルモデル学習状況に更新 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- EN phonemizationをProcessPoolExecutor並列化(60ワーカーで約10倍高速化) - Audio caching Phase 2でキャッシュ済みファイルをスキップ(librosa/torch import不要) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- torch, pytorch-lightning, librosa, cython, pyopenjtalk-plus, wandb を pyproject.toml の dependencies に宣言 - [tool.setuptools.packages.find] where=["src/python"] を追加し piper_train パッケージを正しく認識させる - これにより uv add で新パッケージ追加時に既存依存が削除される問題を防止 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
batch-size 28はV100-16GBでOOM発生。batch-size 20で安定稼働を確認。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- __main__.py: --no-wavlm で WavLM Discriminator を無効化、--wavlm-every-n-steps でWavLM loss計算頻度を制御 - lightning.py: wavlm_every_n_steps 対応、MEMORY_CLEANUP_FREQUENCY を500に変更 - prepare_bilingual_dataset.py: 空phoneme_ids (16件) をスキップ - CLAUDE.md: WavLM無効化の経緯・学習コマンド・状況を更新 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Run `ruff format` (v0.12.5) across all src/python/ files to match CI's `ruff format --check` requirement.
Replace ja_JP-test-medium.onnx with a model fine-tuned from the 6-language base model using CSS10 Japanese dataset (6,841 utterances). The new model supports 173 symbols (multilingual) instead of 65 (JA-only), matching the multilingual training pipeline.
- Rename ja_JP-test-medium.onnx → multilingual-test-medium.onnx (CSS10 fine-tuned from 6-lang base, 173 symbols) - Remove test_voice.onnx (old English-only espeak model) - Update all 55 files referencing old model names
- deploy-huggingface.yml: merge duplicate JA/EN copy blocks into single multilingual model block - deploy-webassembly-demo.yml: remove duplicate CRITICAL_MODELS entry - huggingface-space: consolidate two model entries into one multilingual - simple-multilingual.html: use single model for all languages instead of conditional en_US-test-medium fallback - multilingual.html: update EN button to use multilingual-test-medium - prepare-english-model.sh: simplify to verify multilingual model exists - Remove stale en_US-test-medium references from .gitignore, README, TESTING
Remove 10 stale model files (ja_JP-test-medium, en_US-test-medium, test_voice) from huggingface-space/models/ and src/wasm/openjtalk-web/models/. Replace with multilingual-test-medium.onnx in both directories. Add .gitignore to huggingface-space to prevent future stale model commits. Fix en_US-test.onnx references in test_webui.py.
- docs/cli-enhancements.md: ja_JP-test.onnx → multilingual-test-medium.onnx
- lightning.py: replace hardcoded {0:"ja",1:"en"} fallback with dynamic
lang map derived from num_languages hparam (supports 6-lang models)
- C++ piper: add MultilingualPhonemes type using OpenJTalk phonemization with intersperse padding, add lid (language ID) tensor support - Python runtime: add MULTILINGUAL to PhonemeType enum, handle in phonemize/phonemes_to_ids/synthesize_ids_to_raw with lid support - ruff format: fix 3 files (app.py, download_models.py, lightning.py)
…ilable C++ and Python runtimes now fall back to eSpeak with "en" voice when OpenJTalk is not available for multilingual models. This fixes CI tests that run in environments without OpenJTalk installed. Also fixes ruff format for voice.py.
- main.cpp: initialize eSpeak when phoneme type is MultilingualPhonemes (needed for eSpeak fallback when OpenJTalk is unavailable) - voice.py: provide zero-filled prosody_features tensor when the ONNX model requires it (multilingual model trained with --prosody-dim 16)
The multilingual model expects lid as rank-1 tensor (shape [1]), not rank-2 (shape [1,1]). Remove np.expand_dims wrapper.
ayutaz
force-pushed
the
feat/bilingual-phonemizer
branch
from
March 17, 2026 02:06
0c98d20 to
2bb3849
Compare
…#247) * feat: add C++ multilingual inference support (Phase 1 — lid tensor + config) Add language ID (lid) tensor support to C++ inference pipeline, enabling multilingual ONNX models to run without crashing. Changes: - piper.hpp: Add LanguageId type, MultilingualPhonemes enum, languageId field in SynthesisConfig, numLanguages/languageIdMap in ModelConfig, hasLanguageInput in ModelSession - piper.cpp: Parse num_languages/language_id_map from config.json, detect "lid" ONNX input, construct lid tensor in synthesize() with correct ordering (sid -> lid -> prosody_features), handle "multilingual" and "bilingual" phoneme types, add PUA entries for rr/y_vowel - main.cpp: Add --language/-l CLI option with numeric ID and language code resolution, JSON input language_id/language support, eSpeak config for multilingual models, languageId save/restore in main loop - docs: Add C++ multilingual G2P implementation spec Backward compatible: monolingual models are unaffected (all new code is behind conditional guards on numLanguages/hasLanguageInput). * docs: update C++ multilingual G2P spec — Phase 1 complete Mark Phase 1 as Done with commit hash, actual line numbers, and usage examples. Update gap table, phase table, add implementation details for all 18 change locations. Update Section 4 (ES/FR/PT) with missing rules from review. Mark ARCH-3 as resolved. * feat: add UnicodeLanguageDetector and multilingual phonemization (Phase 2) Add automatic language detection and per-segment phonemization routing for multilingual ONNX models. New files: - language_detector.hpp/cpp: UnicodeLanguageDetector with 6 Unicode range checks (Kana, CJK, Hangul, FullwidthLatin, JaPunct, Latin), text segmentation state machine, CJK disambiguation via kana context, and dominant language detection for lid tensor Changes: - piper.cpp: Add MultilingualPhonemes branch in textToAudio() that segments text by language, routes JA to OpenJTalk and others to eSpeak with correct voice mappings (en-us, cmn, ko, es-la, fr, pt-br), strips BOS/EOS from JA segments, tracks dynamic EOS, sets lid via dominant language detection. Update useProsody and sentenceBoundary conditions. Add streaming fallback. - CMakeLists.txt: Add language_detector.cpp to piper and test_piper Backward compatible: monolingual models are unaffected. * docs: update spec — Phase 2 complete, gap table and appendix refreshed - Mark Phase 2 as Done with commit hash and file details - Expand gap table to show all 10 capabilities with status icons - Add Section 3.1 listing all implemented Phase 2 features - Update Section 3.5 with actual implementation details (file paths, line counts) - Mark ARCH-2 (BOS/EOS double-apply) as resolved in Phase 2 - Update REG-1 scope (Phase 3+ only, no impact on Phase 2) - Note Phase 2 architecture: language_detector in src/cpp/ (not phonemizer/ subdir) * feat: add native C++ G2P for Spanish, French, and Portuguese (Phase 3) Port rule-based phonemizers from Python to C++, replacing eSpeak fallback for ES/FR/PT with Python-accurate G2P. New files (6): - spanish_phonemize.hpp/cpp (782 lines): seseo, yeismo, betacismo, allophonic b/d/g, syllabification, stress assignment - french_phonemize.hpp/cpp (1084 lines): nasal vowels (PUA 0xE056-58), -er/-ille exceptions, silent finals, context-dependent e/o - portuguese_phonemize.hpp/cpp (893 lines): nasal absorption, t/d palatalization (PUA 0xE054-55), coda-l vocalization, BR rules Changes: - piper.cpp: Route ES/FR/PT to native phonemizers (EN/ZH/KO on eSpeak) - CMakeLists.txt: Add 3 new source files to piper and test_piper * feat: add native C++ G2P for English, Chinese, and Korean (Phase 4) English: CMU dictionary-based G2P (123K words, Apache-2.0) - ARPAbet→IPA conversion with context rules (AA+R, ER1/0, AH0) - Function word destressing (89 words) - OOV fallback to eSpeak - Runtime dictionary loading from cmudict_data.json Chinese: pypinyin-based G2P (42K chars + 47K phrases, MIT) - Pinyin→IPA tables (21 initials, 45 finals) - Tone sandhi (T3+T3, yi, bu — 4 rules) - Erhua handling, pinyin normalization (y/w/v) - Runtime dictionary loading from pinyin_single/phrases.json Korean: Hangul decomposition G2P (no external data) - Pure arithmetic decomposition (19×21×28 jamo) - IPA tables (68 entries) with correct neutralization - Basic liaison rule (연음화) - PUA: tense 0xE04B-4F, unreleased 0xE050-52 Integration: - piper.hpp: Add dictionary fields to Voice struct - piper.cpp: Route EN/ZH/KO to native phonemizers in multilingual dispatch, load dictionaries from model directory - CMakeLists.txt: Add 3 new source files - eSpeak fallback when dictionaries are not available Data files (in src/cpp/, loaded at runtime from model directory): - cmudict_data.json (3.7MB, 123K English words) - pinyin_single.json (705KB, 42K Chinese characters) - pinyin_phrases.json (1.9MB, 47K Chinese phrases) * docs: update spec — all 4 phases complete Mark Phase 3 and Phase 4 as Done with commit hashes and file details. Update gap table: all 12 capabilities now have checkmarks. Update file structure to reflect actual implementation (src/cpp/ flat). Mark ARCH-1, EN-1, REG-1 as resolved or not applicable. Add Phase 4 commit info, data file sizes, and line counts. * fix: address critical review findings for C++ multilingual G2P - Unify PUA codepoint mappings: add 57 fixed entries to token_mapper.py covering ZH (43), KO (8), ES/PT (2), FR (3) to match C++ hardcoded values - Fix Chinese phrase dict parser for nested array format [["yí"],["gè"]] - Fix Korean liaison IPA remapping with residualFinal for complex finals - Add LanguageID bounds validation in piper.cpp, main.cpp (CLI/JSON/ONNX) - Strip BOS/EOS from eSpeak fallback segments in multilingual pipeline - Warn when multilingual model missing language_id_map - Prevent dominant language auto-detection from overwriting explicit langId - Add NFD→NFC combining accent collapse for ES/FR/PT phonemizers - Fix Portuguese intervocalic x to recognize accented vowels - Add tʃ/dʒ to Portuguese phoneme inventory, remove unused ø from Korean - Add warnings for unknown phoneme_type and model without lid input * fix: add non-JA prosody extraction and EN OOV eSpeak fallback - Add computeNonJaProsody() for 5 languages (ZH/EN/ES/FR/PT) - ZH: tone(1-5) from PUA markers → a1, syllable position → a2 - EN/ES/PT: stress markers (ˈ→2, ˌ→1) → a2, word phoneme count → a3 - FR: last vowel in word → a2=2 (final-syllable stress) - EN OOV fallback: when CMU dict returns empty, fall back to eSpeak - Replace hardcoded {0,0,0} prosody for non-JA with actual values - docs: add Section 11 with detailed investigation of M1/M4/M7/M11 * docs: update spec to reflect all completed fixes and investigation results - Update header with latest commit ref (0f9429e) - Add EN OOV fallback and non-JA prosody rows to Section 1.1 gap table - Add Review R1/R2 phases to Section 1.2 phase table - Add M11 to Section 7.1 summary table, update M4/M7 status - Add R1/R2 rows to Section 7.3 phase status table - Rewrite Section 7.4 as clean status matrix (done vs pending) - Fix Section 8.2 PUA mapping count (31 → 89 entries) - Update Appendix A priority phases with completion status - Mark Sections 11.2/11.3/11.4 as resolved with commit refs - Update Section 11.4 prosody table (all non-JA now show fixed) - Update Section 11.5 priority matrix with implementation status * refactor: remove piper-phonemize header dependency from public API - piper.hpp: replace 3 piper-phonemize #includes with self-contained type definitions (Phoneme from phoneme_parser.hpp, PhonemeId/PhonemeIdMap as local typedefs) — binary-compatible with piper-phonemize for linking - piper.cpp: move piper-phonemize includes here (implementation-only, not exposed in public header) - Remove tashkeel (Arabic diacritization) dependency entirely: - PiperConfig: remove useTashkeel, tashkeelModelPath, tashkeelState - piper.cpp: remove tashkeel init and runtime code - main.cpp: remove --tashkeel_model CLI option and auto-enable logic piper.hpp is now free of piper-phonemize headers. The runtime still links to piper-phonemize for phonemize_eSpeak() and phonemes_to_ids(). * refactor: replace piper-phonemize/phoneme_ids.hpp with self-contained implementation - Create src/cpp/phoneme_ids.hpp with self-contained phonemes_to_ids() function, PhonemeIdConfig struct, and PhonemeId/PhonemeIdMap typedefs - piper.hpp: include phoneme_ids.hpp instead of phoneme_parser.hpp, remove duplicate PhonemeId/PhonemeIdMap typedefs - piper.cpp: replace piper-phonemize/phoneme_ids.hpp with local header Remaining piper-phonemize dependency: only phonemize.hpp for phonemize_eSpeak() and phonemize_codepoints() runtime functions. * feat: remove eSpeak-ng and piper-phonemize runtime dependencies Complete removal of GPL-licensed eSpeak-ng and piper-phonemize from the C++ inference pipeline. piper-plus now uses only self-contained, Apache-2.0 compatible G2P for all 6 supported languages (JA/EN/ZH/ES/FR/PT). piper.hpp: - Remove eSpeakConfig struct, eSpeakDataPath, useESpeak - Remove eSpeakPhonemes and TextPhonemes from PhonemeType enum - Default phoneme type changed to MultilingualPhonemes piper.cpp: - Remove #include <espeak-ng/speak_lib.h> and <piper-phonemize/phonemize.hpp> - Remove espeak_Initialize()/espeak_Terminate() calls - Remove findEspeakDataPath() (~110 lines) - Remove eSpeakPhonemes and TextPhonemes dispatch branches - Replace eSpeak fallbacks with warnings (EN OOV, unknown language) - Clean up streaming mode to remove legacy code paths main.cpp: - Remove --espeak_data CLI option and help text - Remove espeak-ng.dll/piper_phonemize.dll preloading - Remove eSpeak path resolution logic english_phonemize.cpp: - Add tryMorphologicalFallback() for OOV words (replaces eSpeak fallback) - Supports -ing, -ed, -s/-es/-ies, -er, -ly/-ily, -est suffix stripping - Handles consonant doubling (running→run) and 'e' restoration (making→make) CMakeLists.txt: - Remove piper_phonemize ExternalProject (no more download/build of eSpeak) - Remove link_libraries for piper_phonemize and espeak-ng - Remove include_directories for piper_phonemize - Remove install commands for espeak-ng data and libtashkeel - Significant build time reduction Remaining external C++ dependencies: ONNX Runtime, OpenJTalk, fmt, spdlog * fix: remove last eSpeak fallback reference from OpenJTalk error path * test: add 102 tests for C++ multilingual G2P changes - test_phoneme_ids.py (10 tests): BOS/EOS insertion, inter-phoneme padding, missing phoneme tracking, multi-ID phonemes, edge cases - test_morphological_fallback.py (18 tests): EN OOV suffix stripping for -ing/-ed/-s/-es/-ies/-er/-ly/-est with consonant dedup and vowel restoration - test_non_ja_prosody.py (33 tests): prosody extraction for ZH (tone), EN/ES/PT (stress markers), FR (final-syllable stress), alignment verification for all 5 languages - test_pua_mapping_consistency.py (43 tests): validates all 87 entries in FIXED_PUA_MAPPING with exact codepoints for JA/ZH/KO/ES/PT/FR * fix: update Docker and test CMake for eSpeak/piper-phonemize removal - docker/cpp-dev/Dockerfile: remove eSpeak-ng and piper-phonemize build steps, add ONNX Runtime 1.14.1 direct install - docker/cpp-dev/test.sh: remove eSpeak-ng library and CLI checks - src/cpp/tests/CMakeLists.txt: remove piper_phonemize and espeak-ng from link_libraries and add_dependencies Verified: C++ build succeeds in Docker without eSpeak/piper-phonemize (47/47 targets linked, warnings only, no errors) * fix: add ONNX Runtime ExternalProject for Linux/macOS and clean test.cpp - CMakeLists.txt: add onnxruntime_external ExternalProject for non-Windows platforms (downloads pre-built ONNX Runtime 1.14.1, supports x64/aarch64) - CMakeLists.txt: add ONNX Runtime shared lib install rule for Linux/macOS - src/cpp/test.cpp: remove all eSpeak references (eSpeakDataPath, espeak-ng data path detection), simplify to 2-arg CLI (model + output) Verified: cpp-inference Docker builds and runs inference for all 6 languages (JA/EN/ZH/ES/FR/PT) without eSpeak-ng or piper-phonemize. * style: apply ruff format to token_mapper.py * fix: CI build errors — macOS ONNX Runtime arch name + missing cstdint - CMakeLists.txt: use 'arm64' (not 'aarch64') for macOS ONNX Runtime download URL (onnxruntime-osx-arm64-1.14.1.tgz) - language_detector.cpp: add #include <cstdint> for uint32_t on Ubuntu * fix: CI build errors — test sources, eSpeak enum refs, macOS arch, cstdint - tests/CMakeLists.txt: update include/lib paths from pi/ to ort/ (ONNX Runtime), add missing phonemizer sources to streaming test targets - test_streaming_raw_phonemes.cpp: replace eSpeakPhonemes with MultilingualPhonemes (removed enum value) - test_streaming.cpp: remove useESpeak and TextPhonemes references - CMakeLists.txt: fix macOS ONNX Runtime arch (aarch64→arm64) - language_detector.cpp: add #include <cstdint> for uint32_t * fix: CI failures — Windows ONNX Runtime paths, dict file copy, eSpeak refs - tests/CMakeLists.txt: add ORT_INCLUDE_DIR/ORT_LIB_DIR variables that resolve to ONNXRUNTIME_INCLUDE_DIR (Windows) or ort/ prefix (Linux/macOS); fix self-referencing default values - test-multilingual-tts.yml: add step to copy cmudict/pinyin dict files alongside model before running multilingual inference tests - docker-test.yml: add step to copy dict files to test/models/ before C++ inference tests - test_streaming.cpp: remove TextPhonemes/useESpeak references - test_streaming_raw_phonemes.cpp: replace eSpeakPhonemes with MultilingualPhonemes * fix: remove PARENT_SCOPE from find_onnxruntime_windows (called via include()) * fix: remaining CI failures — multilingual TTS, macOS dylib, JA TTS - test-multilingual-tts.yml: replace eSpeak-based model downloads with direct 6-language tests using bundled multilingual-test-medium.onnx - test-japanese-tts.yml: add DYLD_LIBRARY_PATH for macOS (was Linux-only) - build-piper.yml: explicitly copy ONNX Runtime dylibs/so to dist for macOS and Linux distributions - CMakeLists.txt: add BUILD_RPATH for macOS to find ONNX Runtime during build-time testing, add @executable_path to INSTALL_RPATH * fix: broaden Dictionary Auto-Download grep patterns for OpenJTalk error messages * fix: copy G2P dict files to Docker config fallback test directory * fix: remove eSpeak-ng checks from ARM64 build verification test
10エージェント並列レビューで検出した40件の問題を修正し、 再発防止のための回帰テスト21件を追加。 ## Critical/High 修正 (14件) - piper.cpp: Windows非ASCIIパス破損、PUA音素表示、RTF計算逆転 - Python Runtime: PhonemeType.BILINGUAL追加、lid固定解除、MultilingualPhonemizer対応 - Training: _validate_cache_files実装、language_id assert修正、speaker_id型不一致修正 - ONNX: single-speaker multilingual JSONL sid欠落修正 - Security: model_manager シェルインジェクション対策強化 - C++ UTF-8: 全G2P公開APIに入力バリデーション追加 - pyproject.toml: torch>=2.1.0、onnxruntime>=1.17に修正 ## Medium 修正 (16件) - C++ G2P: portuguese stressIdx調整、korean NFC正規化、spanish xc音素数、 chinese句読点マップ補完、french確認(問題なし) - Training: validation metrics汚染防止、DataLoader shuffle追加、 _build_trainer()ヘルパー抽出 - Phonemizer: chinese.py ImportError化、post_process_ids委譲、 portuguese no-op関数削除 - Runtime: 0.0 falsy修正、Docker espeak残存チェック削除 ## Low 修正 (10件) - utf8_utils.hpp共通化、piper.hpp using json削除・numSpeakers初期化、 norm_audio weights_only、export_onnx unsqueeze除去 + 全squeeze箇所整合 ## 回帰テスト追加 (21件) - C++: PUA codepoint判定、ModelConfig初期化、RTF計算式、UTF-8バリデーション - Python: collate language_id、speaker_id型、sid default、PhonemeType enum、 lid propagation、noise_scale 0.0、pypinyin ImportError、 portuguese stressIdx、korean NFC、spanish xc、chinese句読点、 post_process_ids委譲、validation metrics隔離、ONNX 3D出力、 multilingual phonemizer import、multispeaker transfer
- __main__.py: remove blank line between `import torch` and `from pytorch_lightning` (I001 import block un-sorted) - pyproject.toml: add PLR0911 (too many return statements) to ignore list; voice.py phonemize() has 7 returns due to multi-language fallback chain, consistent with existing PLR0912/PLR0915 ignores
Merge the two phonemize_japanese() return paths (with/without custom dict) into a single conditional expression instead of suppressing the lint rule. Revert PLR0911 from pyproject.toml ignore list.
Characters with IDs 128-255 cannot be represented as valid single-byte UTF-8 (those are continuation bytes, not valid standalone characters). isSingleCodepoint() calls utf8::distance() (checked variant) which throws utf8::invalid_utf8 when given such an invalid byte string. Change the guard from id < 256 to id < 128 so only ASCII codepoints (which are identical in UTF-8) take this path. IPA phonemes like ə (U+0259) and ʊ (U+028A) are multi-byte in UTF-8 and are correctly handled via the phonemeIdToStringMap lookup path instead.
- ruff format: portuguese.py, dataset.py, lightning.py, voice.py - test_multilingual_g2p.cpp: remove piper.hpp (pulls in onnxruntime_cxx_api.h which requires OrtGetApiBase at link time). Replace with phoneme_parser.hpp for piper::Phoneme + inline minimal ModelConfig / isSingleCodepoint / getCodepoint stubs so the test links without onnxruntime.
In extractTimingsFromDurations(), the reverse map (PhonemeId -> string) was built by assigning a char32_t key directly to std::string via std::string::operator=(char), which silently truncates to the lowest byte. For IPA codepoints whose lower byte falls in 0x80–0xBF (e.g. ʊ = U+028A → 0x8A), this stored a bare UTF-8 continuation byte as a standalone string. The subsequent isSingleCodepoint() call passes that string to utf8::distance() (checked variant) which throws utf8::invalid_utf8. Fix: use utf8::append(static_cast<uint32_t>(phonemeChar), ...) to correctly encode any Unicode codepoint as a well-formed UTF-8 string before inserting into the map. This mirrors the pattern already used at line 1386 of piper.cpp.
* feat: add 6-language support to WebUI and HuggingFace demo - docker/webui/app.py: extend language radio to ja/en/zh/es/fr/pt, add has_lid check and language_id_map → lid injection to ONNX inputs, pass language_id_map to text_to_phoneme_ids_and_prosody() - huggingface-space/app.py: expand MODELS dict to all 6 languages, add zh/es/fr/pt fallback chain in text_to_phonemes() via MultilingualPhonemizer → per-language Phonemizer → eSpeak → char, add lid input to ONNX inference, add language_radio UI component, extend Examples with multilingual sample texts - huggingface-space/download_models.py: add language_id_map and num_languages to create_dummy_config() - huggingface-space/requirements.txt: add pypinyin>=0.50 - huggingface-space/app_imports.py: add HAS_PYPINYIN / HAS_G2PK checks - huggingface-space/README.md: document 6-language support, add Supported Languages table and multilingual usage examples - src/python_run/piper/config.py: add num_languages and language_id_map fields to PiperConfig - src/python_run/piper/webui.py: add language_code param to synthesize_speech(), add language selector dropdown, wire language_id through to voice.synthesize() - src/python_run/piper/http_server.py: accept language / language_id query params and pass language_id to voice.synthesize() - src/python_run/piper/sample_texts.py: add zh/es/fr/pt sample texts - pyproject.toml: add huggingface-space/*.py to ruff PLC0415 ignores * feat: add per-language sample texts with auto-fill on language change - docker/webui/app.py: add SAMPLE_TEXTS dict for 6 languages, set initial text to JA sample, wire language.change() to update text_input automatically - huggingface-space/app.py: add SAMPLE_TEXTS dict, wire model_dropdown.change() to on_model_change() which fills text_input with the selected language's sample text - Users can still freely edit the text after auto-fill * feat: add 6-language support to GitHub Pages WebAssembly demo - index.html: expand language buttons to ja/en/zh/es/fr/pt, add languageConfigs for all 6 languages with sample texts, add lid tensor to ONNX feeds, update updateLanguageInfo() for all languages - multilingual.html: expand language buttons, add languageConfigs and langIdMap, add lid tensor, add example texts for zh/es/fr/pt - simple-multilingual.html: expand language buttons and sample texts, add lid tensor to ONNX inference feeds - simple_unified_api.js: add phonemizeChinese() and phonemizeLatinFallback() for zh/es/fr/pt, extend textToPhonemes() routing, enhance detectLanguage() for CJK vs Kana distinction - docker/webui/Dockerfile: add multilingual extras for pypinyin * fix: add missing prosody_features input to HuggingFace demo ONNX inference The multilingual model requires prosody_features as a required input. Add zero-filled prosody_features tensor (shape [1, num_phonemes, 3]) when the model declares this input, matching the existing behavior in docker/webui/app.py and voice.py. * feat: use piper_train phonemize pipeline in HuggingFace demo Replace the standalone text_to_phonemes()/phonemes_to_ids() with piper_train.infer_onnx.text_to_phoneme_ids_and_prosody() which produces correct prosody_features (A1/A2/A3) for Japanese instead of zero-filled tensors, improving Duration Predictor accuracy. - huggingface-space/app.py: remove ~260 lines of duplicated phonemization code, use shared text_to_phoneme_ids_and_prosody() with proper prosody extraction for JA and zero-fill for other langs - deploy-huggingface.yml: copy piper_train/phonemize/ module and infer_onnx.py to HF Space (inference-only, no torch dependency) - requirements.txt: add g2p-en>=2.1.0, replace pyopenjtalk with pyopenjtalk-plus>=0.4 (Apache-2.0) * fix: copy vits/utils.py and wavfile.py to HF Space deployment infer_onnx.py imports from .vits.utils and .vits.wavfile which were missing from the deployment, causing ModuleNotFoundError at startup. * ci: add pre-deployment import validation and HF Space build test - deploy-huggingface.yml: add "Validate Python imports" step that checks all piper_train modules are importable before deploying, aborting on any ImportError (prevents broken deploys) - test-hf-space.yml: new CI workflow with 5 validation layers: Layer 1: core import check (no external deps) Layer 2: install HF Space requirements Layer 3: JA phonemization + prosody test Layer 4: EN phonemization test Layer 5: app.py full import test Triggered on push/PR when HF Space or phonemize files change * fix: use heredoc for inline Python in deploy workflow to fix YAML syntax The inline python3 -c "..." block contained colons that YAML parsed as mapping keys, breaking workflow_dispatch. Switch to heredoc syntax (python3 << 'PYEOF') which avoids YAML interpretation of the script. * fix: move import validation after setup-python to have numpy available * fix: add onnxruntime to import validation deps * fix: download nltk averaged_perceptron_tagger_eng for English g2p * fix: add prosody_features tensor to all WebAssembly demo HTML files The multilingual model requires prosody_features as a required ONNX input. Add zero-filled fallback when prosody data is not available (non-JA languages), preventing 'input is missing' runtime errors. * fix: add BOS/EOS/PAD intersperse to WebAssembly demo phonemesToIds VITS model expects phoneme IDs in the format: [BOS, PAD, ph1, PAD, ph2, PAD, ..., EOS] but the JS implementation was producing: [ph1, ph2, ...] without BOS/EOS framing or PAD intersperse between phonemes. This caused all languages to produce garbled audio on GitHub Pages while HuggingFace (using Python text_to_phoneme_ids_and_prosody) worked correctly. * fix: CI failures — ruff lint/format, test-hf-space layer ordering, g2pk2 import - huggingface-space/app.py: fix I001 (import sort) and add E402 noqa for intentional post-nltk-download imports - pyproject.toml: add E402 to huggingface-space per-file-ignores - test-hf-space.yml: move numpy/pyopenjtalk-dependent imports to Layer 2.5 (after pip install), keep Layer 1 pure-Python only - app_imports.py: fix g2pk -> g2pk2 import probe to match actual dependency (Copilot review) - http_server.py: ruff format fix * fix: download nltk averaged_perceptron_tagger_eng in test-hf-space CI
ayutaz
added a commit
that referenced
this pull request
Mar 18, 2026
- ONNX入力テンソルに lid (language_id) を追加 - config.json スキーマに num_languages, language_id_map を追加 - 7言語 (JA/EN/ZH/KO/ES/FR/PT) のG2P実装方針を追加 - Unicode言語検出とコードスイッチング設計を追加 - PUAマッピングを89エントリ (U+E000-E058) に拡張 - A1/A2/A3プロソディの言語別セマンティクスを文書化 - 推奨クレートに pinyin, hangul, cmudict-fast を追加 - ロードマップ Phase 3 を多言語G2P対応に更新
ayutaz
added a commit
that referenced
this pull request
Mar 19, 2026
- ONNX入力テンソルに lid (language_id) を追加 - config.json スキーマに num_languages, language_id_map を追加 - 7言語 (JA/EN/ZH/KO/ES/FR/PT) のG2P実装方針を追加 - Unicode言語検出とコードスイッチング設計を追加 - PUAマッピングを89エントリ (U+E000-E058) に拡張 - A1/A2/A3プロソディの言語別セマンティクスを文書化 - 推奨クレートに pinyin, hangul, cmudict-fast を追加 - ロードマップ Phase 3 を多言語G2P対応に更新
ayutaz
added a commit
that referenced
this pull request
Mar 19, 2026
* docs: Rust推論実装の調査・設計ガイドを追加 技術スタック選定、アーキテクチャ設計、日本語/英語音素化方針、 ロードマップ、リスク分析を含む包括的なドキュメント。 * docs: Rust推論ガイドを多言語対応 (PR #218) に更新 - ONNX入力テンソルに lid (language_id) を追加 - config.json スキーマに num_languages, language_id_map を追加 - 7言語 (JA/EN/ZH/KO/ES/FR/PT) のG2P実装方針を追加 - Unicode言語検出とコードスイッチング設計を追加 - PUAマッピングを89エントリ (U+E000-E058) に拡張 - A1/A2/A3プロソディの言語別セマンティクスを文書化 - 推奨クレートに pinyin, hangul, cmudict-fast を追加 - ロードマップ Phase 3 を多言語G2P対応に更新 * docs: ロードマップを調査結果と照合し精緻化 - Phase 1: phoneme_type分岐判定・--deviceオプションを追加 - Phase 2: 正規表現パターン・post_process_ids動作を明記 - Phase 3: 6言語G2Pの実装詳細・推定行数・OOVフォールバックを追加、工数を5-7週に修正 - Phase 4: phoneme timing出力・クロスフェードストリーミングを追加 - 工数サマリを実装規模に基づき修正 (合計19-26週) * feat: Rust推論 Phase 1 MVP — ONNX推論エンジン + JSONL入力 + WAV出力 (全20テストパス) Cargo workspace (piper-core / piper-cli) を構築し、 phoneme_ids 直接入力による ONNX 推論パイプラインを実装。 piper-core: - error.rs: PiperError (9バリアント) - config.rs: VoiceConfig パース (多言語 phoneme_type 対応) - engine.rs: OnnxEngine (ort v2 rc12, sid/lid/prosody 条件付きテンソル) - audio.rs: float32→int16 ピーク正規化 + WAV書き出し - input.rs: JSONL パーサー (Python infer_onnx.py 互換) - phonemize/token_map.rs: 87 PUA 固定マッピング (JA/ZH/KO/ES/FR/PT) - phonemize/mod.rs: Phonemizer trait + PhonemizerRegistry piper-cli: - main.rs: clap CLI (--model, --config, --output-dir, --device等) * chore: .gitignore に src/rust/target/ を追加 * feat: Rust推論 Phase 2 — 日本語音素化 + PiperVoice API (全259テストパス) jpreprocess ベースの日本語 Phonemizer を実装し、テキストから直接 音声合成が可能な PiperVoice 高レベル API を追加。 新規モジュール: - phonemize/japanese.rs: 栗原法 prosody マーク、A1/A2/A3 抽出、 疑問詞マーカー (Issue #204)、N変異規則 (Issue #207)、PUA マッピング - phonemize/custom_dict.rs: JSON v1.0/v2.0 カスタム辞書 - phonemize/phoneme_converter.rs: トークン→phoneme_id 変換 - voice.rs: PiperVoice (テキスト→音素化→推論→WAV) - 統合テスト 7 ファイル (123テスト) 修正: - token_map.rs: PUA マッピングを Python token_mapper.py と完全一致に修正 (JA E00A-E015 順序、ZH E031-E045 compound finals) - CLI: --text, --language, --custom-dict オプション追加 * feat: Rust推論 Phase 3 — 7言語 G2P + 多言語Phonemizer (全648テストパス) 6言語の G2P Phonemizer と多言語コードスイッチング基盤を実装し、 テキストから直接音声合成が可能な完全パイプラインを実現。 新規 Phonemizer (6言語): - english.rs: CMU辞書 + ARPAbet→IPA + 形態論OOVフォールバック - chinese.rs: pypinyin辞書 + ピンイン→IPA + 声調サンドヒ + 儿化 - korean.rs: Hangul算術分解 + jamo→IPA + 連音化(liaison) - spanish.rs: ルールベース G2P (seseo, 異音, 音節化, ストレス) - french.rs: ルールベース G2P (鼻母音, 無音末尾子音, 例外辞書) - portuguese.rs: ルールベース G2P (鼻母音, coda-l, t/d口蓋化) 多言語基盤: - multilingual.rs: UnicodeLanguageDetector + MultilingualPhonemizer + default_post_process_ids (BOS/EOS/インタースパースパディング) - voice.rs: Bilingual/Multilingual phoneme type 対応 - CLI: --language バリデーション + 言語自動検出ログ * docs: Rust推論ガイドを実装に合わせて全面更新 - PUAエントリ数を89→87に修正 (3箇所) - ディレクトリ構成を実装に合致させ未実装モジュールを削除 - Phonemizer trait に detect_primary_language 追記 - PiperVoice API シグネチャを実際の引数に更新 - エラー enum を6→10バリアントに更新 - 推奨クレート表に状態列追加 (使用中/Phase 4) - CLI オプション一覧を実装に合わせて更新 - Feature Flags / テストカバレッジ (648テスト) セクション新設 - Phase 1 に完了マーク追加 * feat: Rust推論 Phase 4 — ストリーミング・タイミング・GPU・WASM・PyO3 (全1,227テストパス) Phase 4 の5サブフェーズを実装: 4a: ストリーミング再生 - AudioSink trait + BufferSink + WavFileSink (streaming.rs) - RodioPlayer リアルタイム再生 (playback.rs, feature-gated) - センテンス分割 + クロスフェード (text_splitter.rs) 4b: 音素タイミング出力 - PhonemeTimingInfo + duration→timestamp変換 (timing.rs) - JSON/TSV/SRT 出力フォーマット - engine.rs: duration テンソル抽出 4c: WASM 互換 API - WasmVoice: in-memory モデル読み込み (wasm.rs) - WAV bytes 生成 (ファイルシステム不要) 4d: GPU 推論 - CUDA/CoreML/DirectML/TensorRT ExecutionProvider (gpu.rs) - デバイス列挙 + 自動選択 (device.rs) - engine.rs: GPU プロバイダ統合 + CPU フォールバック 4e: PyO3 Python バインディング - piper-python crate (Cargo.toml + pyproject.toml + lib.rs) - PiperVoice Python クラス + numpy 出力 - GIL 解放による並行推論 追加機能: - モデルダウンロード (model_download.rs, feature-gated) - 音声フォーマット変換 + リサンプリング (audio_format.rs) - バッチ合成 (batch.rs) - CLI: --stream, --timing, --list-devices, --list-models, --batch 新規ソースファイル: 10 (6,570行) 新規テストファイル: 8 (3,946行) 新規 PyO3 クレート: 3ファイル (444行) 変更ファイル: 7 テスト合計: 1,227 パス (Phase 1-3: 648 + Phase 4: 579) * chore: .gitignore に piper-python ビルド成果物を追加 * docs: Rust推論ガイドを Phase 4 実装に合わせて更新 - ディレクトリ構成: Phase 4 の10モジュール + piper-python クレート追加 - Feature flags: 9 features (playback, download, resample, cuda 等) - エラー列挙: 22 バリアント (Phase 4 で8追加) - 推奨クレート: rodio 0.19, reqwest 0.12, rubato 0.16, pyo3, numpy - AudioSink trait (旧 AudioOutput → AudioSink に修正) - SynthesisResult: durations フィールド追加 - Phase 4 ロードマップ ✅ マーク - CLI: --stream, --timing, --list-devices 等の新フラグ - テストカバレッジ: 648 → 1,227 テスト * fix: Phase 4 コードレビュー指摘の critical/major 修正 (全1,232テストパス) レビューで指摘された7件の critical/major 問題を修正: 1. AudioSink trait 重複解消: playback.rs のローカル定義を削除し streaming.rs の正規定義を use で参照 2. CLI --model を Optional に変更: --list-devices, --list-models が モデル指定なしで実行可能に 3. --stream/--batch フラグを実装に接続: - --batch FILE: テキストファイルから一括合成 - --stream --text: センテンス単位の逐次合成 - --text と --batch の排他バリデーション追加 4. GPU 無言フォールバック修正: 明示的デバイス指定("cuda:0"等)の パース失敗時にエラーを返すように変更 (autoは従来通りフォールバック) 5. WavFileSink に Drop 実装: finalize() 忘れ時も WAV ヘッダ更新 6. Duration テンソル抽出にログ追加: 失敗時 warn、成功時 debug 7. DeviceType/DeviceKind ブリッジ: From<DeviceSelection> for DeviceType 変換を追加、両モジュールの役割を doc comment で明確化 * test: TDD監査に基づくテスト135件追加 (全1,367テストパス) TDD監査で指摘された全モジュールのカバレッジギャップを修正: エラー変数テスト (+24): - 全22 PiperError 変数の構築・Display・パターンマッチ - From<io::Error> と From<serde_json::Error> の変換テスト detect_primary_language テスト (+16): - 7言語 (JA/EN/ZH/KO/ES/FR/PT) で未テストだった trait メソッド - KO: get_phoneme_id_map, post_process_ids も補完 wasm エッジケース (+10): - WasmVoice::load_from_bytes エラーパス (無効モデル/config) - i16→f32 境界値、WAV大容量データ、parse_config追加フィールド audio_format エッジケース (+12): - NaN/Inf→i16 変換、zero fade、single sample resample - stereo_to_mono 奇数長、concat crossfade > chunk長 timing エッジケース (+8): - TSV タブ文字、SRT 改行、NaN/Inf duration - Unicode IPA、超小 duration 精度、直接構築 batch/text_splitter (+11): - JSONL missing text field、invalid speaker_id type - nested quotes、CJK句読点のみ、CRLF改行 streaming (+8): - WavFileSink Drop 自動finalize、negative sample crossfade - i16::MAX/MIN crossfade overflow、大容量 BufferSink playback (+8): - double finalize idempotent、sample rate mismatch - 大容量サンプル、複数 sample rate テスト model_download (+9): - zero total bytes progress、empty fields、Unicode名 - cache empty model name、extra JSON fields gpu/device/engine (+12): - auto_detect、whitespace parse、large device_id - SynthesisResult durations、RTF edge case - ModelCapabilities all true/false playback モジュールの feature gate を解除 (DummyPlayer/CollectorSink は rodio 不要のため常時コンパイル可能に) * perf: パフォーマンスチューニング — WAV batch write, テンソルalloc削減, 辞書キャッシュ (全1,371テストパス) HIGH Impact 修正: - WAV batch write: per-sample syscall → 一括write_all (audio.rs, streaming.rs, wasm.rs) - engine.rs: audio_f32中間Vec除去 (slice直接渡し), with_capacity明示化 - CMU辞書/pypinyin辞書: OnceLock化で初回のみロード (english.rs, chinese.rs) - text_splitter: Vec<char> → char_indices() に変更、byte_pos増分追跡 - PyO3: take_audio_int16 (clone除去) + synthesize_batch API追加 - model_download: BufWriter 256KB + builtin_registry OnceLock化 MEDIUM Impact 修正: - resample_linear: per-sample除算 → 増分加算 (step accumulator) - fade_in/fade_out: per-sample除算 → inv_len accumulator - concat_audio crossfade: pre-computed slope - multilingual: bos_eos_tokens/eos_tokens HashSet OnceLock化 - multilingual segment_text: clone → std::mem::take - voice.rs: prosody 4段Vec変換 → build_prosody_direct 1段に統合 - gpu.rs: to_lowercase() → eq_ignore_ascii_case() (allocation除去) - device.rs: enumerate_devices/is_device_available OnceLock キャッシュ - input.rs: JsonlUtterance::to_request move semantics (clone除去) 推定改善: - WAV I/O: 100-500x高速化 (syscall削減) - 辞書ロード: 50-100ms + 15MB削減 (2回目以降) - メモリピーク: 88-176KB/call削減 (中間audio Vec除去) - resample/fade: 2-3x高速化 (除算→加算) * fix: 日本語テストを辞書不在時 graceful skip に修正 (全1,384テストパス) test_japanese_phonemize.rs の create_phonemizer() を require_phonemizer!() マクロに変更。naist-jdic feature 無効時は辞書検索に失敗してもパニック せず早期リターン (SKIP) する。 - naist-jdic 有効: バンドル辞書で実テスト実行 (13テストパス) - naist-jdic 無効: 辞書不在でも全テスト ok (graceful skip) - どちらの場合も 1,384 テスト全パス、0 失敗 * ci: Rust CI/CD ワークフロー追加 rust-tests.yml (スタンドアロン): - cargo check --workspace --all-features - cargo test (default + naist-jdic) on ubuntu/macos/windows - cargo fmt --check - cargo clippy -D warnings - maturin build (Python バインディング) ci.yml (メイン CI に統合): - rust-tests ジョブを追加 (ubuntu + macos) - fmt, clippy, test, naist-jdic test, piper-python check * fix: Copilot レビュー指摘10件を全修正 (全1,394テストパス) 1. custom_dict.rs: pattern_cache を Mutex<HashMap> に変更し実際にキャッシュ 2. input.rs: 空行スキップの再帰をループに変更 (スタックオーバーフロー防止) 3. playback.rs: doc example の AudioSink import パスを streaming に修正 4. device.rs: cpu:N (N!=0), coreml:N (N!=0), 負ID を拒否 5. engine.rs: doc comment から未対応の "gpu" を削除、正しいデバイス名を記載 6. streaming.rs: WavFileSink sample_rate 不一致時にエラー返却 7. streaming.rs: WAV u32 オーバーフロー検出 (4GB超でエラー) 8. streaming.rs: crossfade alpha を i/(overlap-1) に修正 (1.0到達) 9. model_download.rs: reqwest に connect/read timeout 設定 (30s/600s) 10. model_download.rs: url_filename がクエリ文字列・フラグメントを除去 * fix: CI失敗を修正 (rustfmt, maturin python-source, ort-sys glibc) - cargo fmt --all で全ソースをフォーマット統一 - piper-python pyproject.toml から python-source 設定を削除 (ディレクトリ不在エラー) - CI の ubuntu-22.04 を ubuntu-24.04 に変更 (ort-sys が glibc 2.38+ を要求) * fix: CI失敗を修正 (alsa-sys, PyO3 Python 3.14, unused imports) - rust-tests.yml: libasound2-dev インストール追加 (rodio/alsa-sys ビルド用) - rust-tests.yml/ci.yml: setup-python 3.12 追加 (PyO3 0.22 は Python 3.14 非対応) - audio_format.rs: PiperError import を #[cfg(feature = "resample")] で条件付きに - voice.rs: build_prosody_direct を #[cfg(test)] で条件付きに - model_download.rs: url_filename を #[cfg(any(feature = "download", test))] で条件付きに * fix: AudioSink から不要な Send 境界を削除 + CI から libasound2-dev 依存を除去 AudioSink トレイトの Send 境界は、コードベース全体でスレッド間転送が 一切行われないため不要。この境界が Linux CI で cpal/rodio の !Send (ALSA バックエンド) と衝突しビルド失敗を引き起こしていた。 10エージェントによる調査で以下を確認: - AudioSink は thread::spawn / tokio::spawn / async で一切使用されない - Box<dyn AudioSink> のインスタンスは存在しない - 全使用箇所は &mut dyn AudioSink のローカル参照のみ * fix: rust-tests CIにlibasound2-devを復元 --all-features ビルドで playback feature → rodio → alsa-sys が 必要なため、check/clippy ジョブに libasound2-dev インストールを追加。 * fix: clippy警告35件を修正 - multilingual.rs: repeat().take() → repeat_n() - phoneme_converter.rs: too_many_arguments を allow - portuguese.rs: collapsible if, bool simplification, &mut Vec→&mut [char], enumerate - spanish.rs: identical if/else blocks, needless range loop - device.rs: FromStr trait を正式実装 - wasm.rs: needless late init を直接初期化に - playback.rs: needless return を除去 * fix: clippy 15件 + rustfmt 4件を修正 - portuguese.rs: rustfmt brace位置修正 + int_plus_one - engine.rs: needless_late_init 3件 + 不要な参照除去 - chinese.rs: identical if blocks統合 + complex type alias - french.rs: collapsible if, bool simplification, needless range loop 2件 - japanese.rs: &mut Vec → &mut [String] - korean.rs: RangeInclusive::contains 使用 - multilingual.rs: 不要な明示的ライフタイム除去 * fix: piper-python clippy 7件修正 - SendPtr::as_mut: mut_from_ref を allow (unsafe設計上必要) - synthesize/synthesize_batch: too_many_arguments を allow (PyO3 API) - to_pyresult削除: useless conversion警告の原因 → 直接 .map_err() に置換 - save_wav: 同上 * fix: piper-python useless conversion根本修正 + rustfmt clippy useless_conversion の根本原因: .map_err().map() チェーンが PyErr→PyErr の無意味な変換と判定されていた。 Ok(result.map_err(piper_err_to_pyerr)?.into()) パターンに統一し、 cargo fmt で正確なフォーマットを適用。 * fix: piper-python clippy useless_conversion を impl ブロックで allow PyO3 の #[pymethods] が自動で From<PiperError> -> PyErr 変換を挿入するため、 明示的な map_err(piper_err_to_pyerr) が clippy に「同型への無意味な変換」と 判定される。カスタム例外マッピング (ValueError/IOError/RuntimeError) を 維持するため、impl ブロック全体に allow を適用。 * fix: Cargo.toml [lints.clippy] で useless_conversion を allow #[pymethods] マクロ展開後のコードに #[allow] attribute が伝播しない問題。 Cargo.toml の [lints.clippy] セクションでクレートレベルで allow することで 確実に clippy useless_conversion を抑制。
ayutaz
added a commit
that referenced
this pull request
May 2, 2026
二次監査で「低優先度・別 PR 推奨」と棚上げした 5 項目に対応。レガシー API (BILINGUAL/espeak) の意図と廃止予定を明示し、サンプル/パッケージのバージョン 管理状況をユーザーが誤解しないようドキュメント化する目的。 - Go (src/go/piperplus/config.go): PhonemeTypeEspeak / PhonemeTypeBilingual に Deprecated コメント追加 (CONTRIBUTING.md "espeak-ng Policy" / PR #218 への参照付き) - Python (src/python_run/piper/voice.py): PhonemeType.BILINGUAL 分岐に Deprecation 注記 + SV/KO が学習済みモデル未対応の理由をコメント化 - C# (src/csharp/PiperPlus.Core/Config/PiperConfig.cs): Espeak フィールドに XML doc 追加 (legacy config.json 互換のみ、新規モデルは null とする旨) - examples/{c-api,dart,godot}/README.md: CI で実行検証されているか否かを 各 README 冒頭に明示 (c-api はビルドのみ検証、dart/godot は未検証) - CONTRIBUTING.md: "Package Versioning Policy" セクション新規追加 (8 パッケージそれぞれの独立バージョン管理ポリシーとタグ命名規則を表形式で明文化) 実装ロジックには変更なし (コメント・docstring・README のみ)。
ayutaz
added a commit
that referenced
this pull request
May 3, 2026
* docs: 監査結果に基づくドキュメント全面同期 v1.11.0 以降の実装変更 (#321 OpenAI互換API, #331 Voice Cloning/SSML, #337 短文品質, #342 HTS voice除去, #349/#367 phoneme timing/streaming, #361 FastAPI移行, #366 arm64 Docker) が CHANGELOG・多言語 README・docstring に未反映だった部分を一括同期。新規に C# CLI/Core README を追加。 - ルート CHANGELOG: Unreleased に #360-#366 (FastAPI移行/arm64 Docker/短文崩壊修正/Wyoming HA等) を追記 - 多言語 README 9 ファイル (DE/ES/FR/HI/KO/PT/RU/SV/ZH): Voice Cloning/SSML/Phoneme Timing/Strategy A/B/C/FastAPI を反映 - C# CLI/Core README 新規作成 (src/csharp/PiperPlus.Cli/, PiperPlus.Core/) - WASM CHANGELOG: [0.3.1] セクション追加 + [0.4.0] 日付修正 (0.3.0 と同日問題解消) - docstring 補強: http_server.py FastAPI エンドポイント / inference.py OpenAI 互換 API - HTS voice 残存言及を削除 (windows-setup.md) - --language-balanced-sampling help を 6 言語対応に更新 (バイリンガル時代の遺物修正) - Go README: Docker multi-arch (amd64/arm64) + serve サブコマンドのセクション追加 - pretrained-models.md: つくよみちゃん 6lang-v2 / 6lang ベースモデルの詳細追記 - huggingface-space/docker/README: 言語表記/Go arm64 表記の整合修正 * docs: 二次監査で発見した取り残しを追加修正 前回コミット (f212aba) では多言語 README 9 ファイルを更新したが、 ベースとなる README.md (日本語) と README_EN.md には Voice Cloning / SSML / 短文品質改善の bullets を入れ忘れていた。 また Rust piper-core/src/lib.rs のクレートドキュメントが「7 言語 (KO 含むが SV を欠く)」のまま、 8 言語 G2P + 6 言語学習済みモデルという最新の状態を反映していなかった。 - README.md / README_EN.md のインターフェースセクション末尾に Voice Cloning / SSML / 短文品質改善 (Strategy A/B/C) の 3 bullets を追加 (多言語版と同等内容) - src/rust/piper-core/src/lib.rs:4 のクレートドキュメントを「8 言語 G2P (JA/EN/ZH/KO/ES/FR/PT/SV)、学習済みモデルは 6 言語」に修正 二次監査で誤指摘と判明した項目はスキップ: - WASM g2p test の "all 7 non-JA languages" は JA を除いた 7 言語の意味で正しい (ALL_NON_JA = en/zh/ko/es/fr/pt/sv) - C# CLI README の --debug/--quiet/--version はすでに L66 に記載済み * docs: 低優先度の取り残し項目をすべて対応 二次監査で「低優先度・別 PR 推奨」と棚上げした 5 項目に対応。レガシー API (BILINGUAL/espeak) の意図と廃止予定を明示し、サンプル/パッケージのバージョン 管理状況をユーザーが誤解しないようドキュメント化する目的。 - Go (src/go/piperplus/config.go): PhonemeTypeEspeak / PhonemeTypeBilingual に Deprecated コメント追加 (CONTRIBUTING.md "espeak-ng Policy" / PR #218 への参照付き) - Python (src/python_run/piper/voice.py): PhonemeType.BILINGUAL 分岐に Deprecation 注記 + SV/KO が学習済みモデル未対応の理由をコメント化 - C# (src/csharp/PiperPlus.Core/Config/PiperConfig.cs): Espeak フィールドに XML doc 追加 (legacy config.json 互換のみ、新規モデルは null とする旨) - examples/{c-api,dart,godot}/README.md: CI で実行検証されているか否かを 各 README 冒頭に明示 (c-api はビルドのみ検証、dart/godot は未検証) - CONTRIBUTING.md: "Package Versioning Policy" セクション新規追加 (8 パッケージそれぞれの独立バージョン管理ポリシーとタグ命名規則を表形式で明文化) 実装ロジックには変更なし (コメント・docstring・README のみ)。 * docs: Copilot レビュー指摘 16 件 + 追加発見をすべて対応 Copilot pull-request reviewer が PR #368 に付けた 16 件のレビューコメントと、 それを契機にした再監査で見つかった整合性問題を一括解消。リリース直前の 一貫性確認に過ぎないので、いずれもドキュメント・コメントレベルの調整のみ。 Copilot 指摘 (16件): - 多言語 README 11 ファイル (README.md/EN/DE/ES/FR/HI/KO/PT/RU/SV/ZH): bullets が 「7 ランタイム」と書きながら列挙は 6 つだったのを「6 ランタイム」に統一。 日本語版/英語版は libpiper_plus を C++ にまとめる旨を補足 - docker/python-inference/inference.py: モジュール docstring が定義されていない `POST /api/phoneme-timing` を載せ、`/synthesize` を POST と書いていたのを修正 (該当エンドポイントは src/python_run/piper/http_server.py 側であることも明記) - src/python/piper_train/__main__.py:255-260: `--language-balanced-sampling` が `num_speakers > 1` も必要 (single-speaker は SpeakerBalancedBatchSampler を バイパス) という条件を help に追記 - docker/README.md:19: Go Dockerfile 行の base image を `golang:1.22` から 実際の `golang:1.26` に修正 - src/go/README.md:380-: `docker buildx build` の例に `--load` (single-arch) と `--push` (multi-arch) の両パターンを示し、ローカル実行不可だった例を解消 - src/go/README.md:298 / 406: 見出し「## HTTP API / HTTPエンドポイント」を 「## HTTP API」に変更してアンカーを `#http-api` に統一、リンク `[HTTP API](#http-api--http-api)` の broken anchor を修正 - src/csharp/PiperPlus.Core/README.md:34: `new DotNetG2PEngine()` は PiperPlus.Cli の `internal sealed class` で外部から呼べないため、 IJapaneseG2PEngine を持ち込む必要がある旨を明記したサンプルに置換
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Piper TTS を日英バイリンガルから 6言語マルチリンガル (JA/EN/ZH/ES/FR/PT) に拡張。Python 学習パイプラインと C++ 推論エンジンの両方で GPL フリーの多言語対応を実現。
主な変更点
Python: 多言語 Phonemizer + 学習パイプライン
MultilingualPhonemizer: Unicode 言語検出で文内コードスイッチングに対応nn.Embedding(n_languages, gin_channels)を VITS に統合lid(language ID) テンソル対応--freeze-dp: Duration Predictor 凍結でファインチューニング時の catastrophic forgetting 防止--resume-from-multispeaker-checkpoint: マルチ→シングル話者転移を1フラグで自動化C++: GPL-free マルチリンガル G2P エンジン (#247)
LanguageDetectorで混合テキストを自動セグメント分割その他
lidテンソル、eSpeak フォールバック)変更規模
Test plan