Commit 0c85bfb
feat: add pinyin support (chaowen, xiao_ya) (#46)
* feat: add pinyin support (chaowen, xiao_ya)
- bump piper-objc to 0.2.35 for piper_create_with_options + PiperCreateOptions
- use PiperCreateOptions with dataDir/g2pwModelDir from model folder to enable Chinese pinyin voices
- remove chaowen/xiao_ya from unsupportedVoices now that piper-objc supports them
Requires piper-objc PR #10 merged (dadde0b) which is in 0.2.35
* feat: on-demand g2pw Phase 1 for Chinese pinyin (2MB lean)
- Add phoneme_type to ModelInfo with isPinyin flag to detect pinyin voices
- Add shared g2pw folder in App Group (group.pipertts.data/g2pw) for on-demand 2MB Phase 1 dicts
- Add G2PWDataManager: copies from bundle (offline) or downloads 3 files from GitHub release g2pw-mini-v1
(MONOPHONIC_CHARS.txt 118K, char_bopomofo_dict.json 1.8M, bopomofo_to_pinyin_wo_tune_dict.json 7.6K)
- VoiceLoader: ensure g2pw installed on first Chinese voice download (pinyin)
- PiperTTSAudioUnit: use shared g2pw folder for pinyin voices, fallback to model folder
- Bundle mini dicts in PiperAppUtils/Resources/g2pw for offline CI and fast first-copy (2MB, no RAM until used)
- Hosted assets: https://github.com/IhorShevchuk/piper-app/releases/tag/g2pw-mini-v1
- Keeps IPA lean, RAM tiny (no 152M onnx), covers 95% monophonic chars for chaowen/xiao_ya
* feat: pure on-demand g2pw download (0 IPA)
- Remove bundled g2pw dicts from IPA (1.9M) – keep IPA lean per user request
- G2PWDataManager now pure download from GitHub release g2pw-mini-v1
(3 files: MONOPHONIC_CHARS.txt, char_bopomofo_dict.json, bopomofo_to_pinyin_wo_tune_dict.json)
- Voice install already requires network, so no extra failure mode
- Once cached in group.pipertts.data/g2pw, synthesis works offline
- PiperTTSAudioUnit uses shared g2pw folder for pinyin voices, fallback to model folder
- Keeps RAM tiny (no 152M onnx), covers 95% monophonic chars for chaowen/xiao_ya
* feat: isolate pinyin g2pw changes to PiperApp folder (pure on-demand 0 IPA)
- Keep all new logic in PiperApp/Sources/Utils/G2PWDataManager.swift (pure download from GitHub release g2pw-mini-v1)
- VoiceLoader in PiperApp detects zh voices via language code + JSON phoneme_type, ensures g2pw on first zh install
- FileManager.install in PiperApp copies shared g2pw dicts into voice folder for offline synthesis
- PiperAppUtils/FileManager/FIleConstants.swift: add shared g2pwFolderURL + required files (minimal shared)
- PiperTTS: use modern PiperCreateOptions with dataDir/g2pwModelDir for zh voices (prefers shared g2pw folder, fallback to model folder)
- Revert ModelInfo isPinyin (detect via JSON directly in VoiceLoader to keep PiperAppUtils unchanged)
- Remove bundled g2pw resources – 0 IPA hit, RAM tiny, covers 95% monophonic chars for chaowen/xiao_ya
- Comments updated to explain on-demand download (voice install already needs network, so no extra failure mode)
* feat: option 5 – xcassets compressed g2pw (300K IPA, offline, no server)
- Add PiperApp/Resources/G2PW.xcassets with 3 data assets:
MONOPHONIC_CHARS.txt (118K), char_bopomofo_dict.json (1.8M), bopomofo_to_pinyin_wo_tune_dict.json (7.6K)
- Asset catalog compresses to ~300K in IPA (vs 1.9M raw), lazy-loaded via NSDataAsset (zero RAM until first zh voice)
- G2PWDataManager now primary copies from xcassets NSDataAsset to App Group g2pw/ (offline, no network)
- Fallback to GitHub release download if asset not found (tests)
- VoiceLoader + FileManager.install keep same flow: ensure g2pw in shared folder, copy into voice folder for offline synthesis
- Project.swift: include G2PW.xcassets in PiperApp target resources
- Keeps changes isolated to PiperApp folder (plus minimal shared constants in PiperAppUtils for App Group)
- No external server, no download failures, works offline, covers 95% monophonic chars for chaowen/xiao_ya
* feat: drop fallback download – pure xcassets offline (option 5)
- Remove GitHub release download fallback from G2PWDataManager
- ensureInstalled() now only copies from NSDataAsset in G2PW.xcassets to shared g2pw/
- No network, no URLSession, no GitHub dependency – pure offline, 300K compressed IPA
- VoiceLoader calls synchronous ensureInstalled() (no await) on first zh voice install
- Keeps RAM tiny, covers 95% monophonic chars for chaowen/xiao_ya
* chore: remove duplicate G2PWDataManager – keep only PiperApp version
- Delete PiperAppUtils/G2PW/G2PWDataManager.swift (duplicate)
- Keep single source of truth in PiperApp/Sources/Utils/G2PWDataManager.swift (option 5 xcassets)
- PiperAppUtils only keeps shared FileManager.Constants.g2pwFolderURL + required files
* chore: update outdated comments for option 5 xcassets
- VoiceLoader: remove 'download 2MB' wording, now xcassets offline
- FileManager.install: simplify comment (no longer mentions isolating to PiperApp)
- PiperTTSAudioUnit: update to xcassets wording, not on-demand download
- FIleConstants: update MARK comment from 0 IPA to 300K IPA xcassets compressed
* chore: drop unneeded comments to avoid staleness
- Remove explanatory comments in G2PWDataManager, VoiceLoader, FileManager, PiperTTSAudioUnit, FIleConstants
- Keep code self-explanatory, no option numbers or IPA size in comments
- Prevents stale docs as implementation evolves
* fix: build + simplify pinyin detection
- Fix G2PW.xcassets dataset Contents.json: idiom universal (was invalid universal:true causing exit 65)
- Rename fm to fileManager, fix implicit optional init, vertical whitespace
- Simplify isPinyinVoice to just language code check (zh prefix), drop Data(contentsOf:) + JSONSerialization
- Reduce VoiceLoader function_body_length via helper extraction
* fix: remaining fm identifier_name
- Fix copyToModelFolder still using let fm (was causing fileManager not found)
- Now zero fm violations in PR files
* refactor: use Decodable VoiceConfig for pinyin detection
- Add Models/VoiceConfig.swift Decodable with phoneme_type
- Replace JSONSerialization/Any cast closure with JSONDecoder decode
- Keep zh prefix fast path, fallback to decoded phonemeType == pinyin
- Addresses fm identifier_name warning by removing manual parsing clutter
* fix: increase audio buffer from 5s to 120s to prevent middle-drop on long utterances
- 5s max caused FloatRingBuffer to drop oldest unread samples when Piper
synthesizes faster than AU consumes, leading to 'starts then skips to end'
on long sentences/paragraphs (#24, reported on pinyin branch)
- Increase maxBufferDurationSeconds to 120s (~2.6M floats, ~10MB worst case)
to hold typical long Speak Screen / VoiceOver continuous reading
- Keeps max enforcement for OOM safety but large enough for real use
* Fixed a few build errors
* chore: bump piper-objc to 0.2.37 for relaxed poly fallback, restore full Package.swift
* chore: update Package.resolved for piper-objc 0.2.37
* fix: Samples.json zh_CN to simplified, only piper-readable symbols
* chore: bump piper-objc to 0.2.38 for linker fix (chinese_phonemizer)
* added missed new line
---------
Co-authored-by: Ihor Shevchuk <ihor@ihor-shevchuk.dev>1 parent d02035a commit 0c85bfb
20 files changed
Lines changed: 145767 additions & 61 deletions
File tree
- PiperAppUtils
- FileManager
- PiperApp
- Resources
- Assets.xcassets/Samples.dataset
- G2PW.xcassets
- MONOPHONIC_CHARS.dataset
- bopomofo_to_pinyin_wo_tune_dict.dataset
- char_bopomofo_dict.dataset
- Sources
- Utils
- VoiceLoading
- Models
- PiperTTS/Sources/AudioUnit
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | | - | |
| 21 | + | |
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
Lines changed: 43 additions & 43 deletions
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
Lines changed: 12 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
0 commit comments