Instructions to use watchpen/piper-plus with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Piper
How to use watchpen/piper-plus with Piper:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
|
Download README.md from watchpen/piper-plus: direct link, hf CLI and curl.
- Browser
- Download file 47.1 kB
-
https://huggingface.co/watchpen/piper-plus/resolve/main/README.md
- Command line
-
hf download hf://watchpen/piper-plus/README.md
-
curl -L -o README.md https://huggingface.co/watchpen/piper-plus/resolve/main/README.md
47.1 kB
| language: | |
| - ko | |
| license: other | |
| license_name: piper-plus | |
| pipeline_tag: text-to-speech | |
| library_name: onnx | |
| tags: | |
| - text-to-speech | |
| - tts | |
| - piper | |
| - vits | |
| - onnx | |
| - korean | |
| - neural-resonance | |
| - hunminjeongeum | |
| # Piper Plus โ ๋์๋ฆฌ ๊ณผ์ ์ผ๋ฐํ๋ฅผ ์ต์ ํ๊ณ ์ ๊ฒฝ๊ณต๋ช ์ผ๋ก ๊ฐ์ ์ ํํํ๋ ํ๊ธ ์๋ชจ ์ํ ๋ฐ์ฑ ๋ชจ๋ธ | |
| ### *Piper Plus: Korean Neural-Resonance TTS with Fortis-Overgeneralization Suppression, Emotion-Expressive Neural Resonance & Acoustic Jamo Vocalization* | |
| > ๐ข **[2026-10-24 ๋ชจ๋ธ ๊ฐ์ค์น ์ ๋ฉด ๊ต์ฒด ์๋ด]** | |
| > ์ด์ ๊ฒฝ๊ณ๋ฅผ ์ ๊ฑฐํ๊ณ ๋์๋ฆฌ ๊ณผ์ ์ผ๋ฐํ๋ฅผ ์ต์ ํ ์ต์ **Epoch 880 EMA (`piper-plus.onnx`)** ๋ชจ๋ธ๋ก ๊ต์ฒด๋์์ต๋๋ค. ์ด์ ์ ๋ค์ด๋ก๋ํ์ ๋ถ๋ค์ ์ต์ ๋ชจ๋ธ๋ก ๋ค์ ๋ฐ์์ฃผ์๊ธฐ ๋ฐ๋๋๋ค. | |
| > | |
| > ๐ข **[2026-10-24 Model Weights Updated]** | |
| > Replaced with the latest **Epoch 880 EMA (`piper-plus.onnx`)** model (word-boundary `eojeol_boundary` CumSum completely removed & excessive fortis/tensification generalization structurally suppressed). If you downloaded an earlier version, please re-download `piper-plus.onnx`. | |
| > | |
| > โ ๏ธ **[๋น์์ ์ฉ ์ฌ์ฉ ์ ํ ๋ฐ ์ ์์ ํ์ ์๋ฌด (Non-Commercial Use Only & Mandatory Attribution)]** | |
| > - **๋น์์ ์ฉ ์ ์ฉ (์์ ์ ์ฌ์ฉ ์ ๋ณ๋ ํ๊ฐ ํ์)**: ๋ณธ ๋ชจ๋ธ(`piper-plus`)๊ณผ ํฌํจ๋ ๊ณต๋ช ์ฃผํ์ ํยท์ฝ๋ยท๊ฐ์ค์น๋ **๋น์์ ์ฉ(๊ฐ์ธ ํ์ตยทํ์ ์ฐ๊ตฌยท๋น์๋ฆฌ ๋ชฉ์ )์ผ๋ก๋ง ๋ฌด๋ฃ ์ฌ์ฉ์ด ๊ฐ๋ฅ**ํ๋ฉฐ, **์์ ์ ์ฌ์ฉ(์ ๋ฃ ์ฑยท์น ์๋น์ค ํ์ฌ, ์๋ฆฌ ๋ชฉ์ ์ฝํ ์ธ ์ ์ยท๋ฐฐํฌ, ๊ธฐ์ ์ฉ ํ์ฉ ๋ฑ)์ ๋ฐ๋์ ์์ ์์(`watchpen`)์ ๋ณ๋ ์ฌ์ ํ๊ฐ**๋ฅผ ๋ฐ์์ผ ํฉ๋๋ค. | |
| > - **์ ์์ ํ์ ๋ฐ ๊นํ๋ธ ์ฃผ์ ์ฒจ๋ถ ์๋ฌด**: ๋ณธ ๋ชจ๋ธ(`piper-plus`), ์ ๊ฒฝ๊ณต๋ช (๋ฐ์ยท์ฑ์ฐยท๊ฐ์ ๊ณต๋ช ์ฃผํ์ ํ), ์๋ชจ ์ํ ๋ฐ์ฑ ๊ตฌ์กฐ, ์ ๋ก๋๋ ์ด์ ํ๊ทธ ์ฒด๊ณ, ๋๋ ํฌํจ ์ฝ๋/๊ฐ์ค์น๋ฅผ **์์ฉํ๊ฑฐ๋ ์์ ยทํ์ยท์ฌ๊ฐ๋ฐํ ๊ฒฝ์ฐ, ๋ฐ๋์ ์์ ์์ ์ด๋์ (`watchpen`)๊ณผ ๊ณต์ ๊นํ๋ธ ์ฃผ์(https://github.com/wacthpen/piper-plus)๋ฅผ ํจ๊ป ์ฒจ๋ถ(๋ช ์)**ํด์ผ ํฉ๋๋ค. | |
| > | |
| > * **Non-Commercial Use Only (Separate Permission Required for Commercial Use)**: This model (`piper-plus`), its neural resonance tables, weights, and codebase are provided **strictly for non-commercial use (personal, academic, and non-profit research)**. **Any commercial use requires prior separate permission from the original author (`watchpen`).** | |
| > * **Mandatory Attribution**: If you adapt, modify, fine-tune, or redevelop this model, its neural resonance tables, or its codebase, you MUST explicitly credit and include the original author's handle/initials (`watchpen` / GitHub: `wacthpen`, Hugging Face: `watchpen`) AND attach the official GitHub repository URL (`https://github.com/wacthpen/piper-plus`) in your documentation, model card, and derivative works. | |
| --- | |
| ## ๐ Piper Plus ํต์ฌ ์ ์ฒด์ฑ (Core Identities) | |
| 1. **๋์๋ฆฌ(๊ฒฝ์ํ) ์ผ๋ฐํ ๊ณผ์ ์ต์ *(Suppression of Excessive Fortis / Tensification Generalization)*** | |
| - ๊ธฐ์กด ํ๊ตญ์ด TTS์ ๋ถ์์ ํ ์ธ๋ถ G2P๊ฐ ๋ง์ฐํ๊ฒ ์ผ์ผํค๋ ๊ณ ์ง๋ณ์ธ **'์์ฌ์๋ฆฌ์ ๊ณผ๋ํ ๋์๋ฆฌ(๊ฒฝ์ํ) ์ผ๋ฐํ'**๋ฅผ ์์ฒ ์ต์ ํ ๋ชจ๋ธ์ ๋๋ค. | |
| - ์ ๊ฒฝ๋ง์ ๋ฌธ๋งฅยท์๋ฒ ๋ฉ ๊ธฐ์ต์ธต(Memory Layer)์ ๊ทธ๋๋ก ํต๊ณผ์ํค๋ฉด ํ์ต ๋ฐ์ดํฐ ์ ๊ฒฝ์ํ ํ์์ด ๊ณผ์ ์ผ๋ฐํ๋์ด ๋ฉ์ฉกํ ์์ฌ์๋ฆฌ(ํ์ `ใฑ, ใท, ใ , ใ , ใ `)๊น์ง ๋์๋ฆฌ(`ใฒ, ใธ, ใ , ใ , ใ `)๋ก ์ค์ผ๋ฉ๋๋ค. **Piper Plus๋ ์ถ๋ ฅยทํ ์คํธ ์ธ์ฝ๋์์ '๋์๋ฆฌํ๋ ๊ธฐ์ต์ธต(`self.emb`)'์ ํต๊ณผํ์ง ์๋๋ก(100% ๊ณต๋ช ์ฃผํ์ ๋์ฒด ๋ชจ๋๋ก ์ฐํ) ์ค๊ณ**ํ๊ณ **์์ฌ์๋ฆฌ 24๋ฐฐ ๋ณดํธ ์ธ๊ธฐํ๋ณ๊ธฐ**๋ฅผ ๊ฒฐํฉํด ์ ๊ฒฝ๋ง์ด ์์๋ก ๋์๋ฆฌ๋ฅผ ๋ง๋ค์ด๋ด๋ ํ์์ ๊ตฌ์กฐ์ ์ผ๋ก ์์ ํ ํผํด๊ฐ์ผ๋ฉฐ, ์ค์ ๋ก ์๋ฆฌ๊ฐ ๋ฐ๋์ด์ผ ํ๋ **๋ถ๊ท์น ๋ฐ์์ ๋ฐ์์ฌ์ (Dictionary)์ผ๋ก 1:1 ์ ๋ฐ ๋์**ํฉ๋๋ค. | |
| 2. **๊ฐ์ ํํ์ด ๊ฐ๋ฅํ ์ ๊ฒฝ๊ณต๋ช & ๋ฌธ๋งฅ ๊ฐ์ ๋ชจ๋ธ ์๋ ๋งคํ *(Emotion-Expressive Neural Resonance & Context-Aware Auto Emotion Mapping)*** | |
| - ๋จ์ํ ํ๋ฉด์ ํ ์คํธ ๋ญ๋ ์ ๋์ด, 32๋ ๊ฐ์ ยท๋งํฌ ์ฒด๊ณ์ **5๋ ๋ฐ์ฑ ๋ฌผ๋ฆฌ ์ถ(๊ณต๋ช ๊ท ํ, ์ฑ๋ฌธ ๊ธด์ฅ๋, ํธํก๊ธฐ์, ํผ์นยทํฌ๋จผํธ ์ด๋, ๋ฐํ ์๋)**์ด ๊ฒฐํฉ๋ **'์ ๊ฒฝ๊ณต๋ช (Neural Resonance)'** ์ํคํ ์ฒ๋ก ์ด์ ์จ ์ฌ๋ ๊ฐ์ ์ฐ๊ธฐ๋ฅผ ๊ตฌํํฉ๋๋ค. | |
| - ํนํ ์์ฐ์ค๋ฌ์ด ๊ฐ์ ํํ์ ์ ๋ํ๊ธฐ ์ํด **์ ์ฉ ํ๊ตญ์ด ๋ฌธ๋งฅ ๊ฐ์ ๋ถ๋ฅ ๋ชจ๋ธ(`emotion/emotion_int8.onnx`)์ ์ง์ ํ์ต**ํ์ฌ ํ์ฌํ์ต๋๋ค. ์ฌ์ฉ์๊ฐ ์ผ์ผ์ด ๊ฐ์ ํ๊ทธ๋ฅผ ์๋์ผ๋ก ์ ์ง ์์๋, ๊ฐ์ ๋ชจ๋ธ์ด **๋ฌธ์ฅ์ ์๋ค ๋ฌธ๋งฅ(Context)์ ์ฝ๊ณ ์๋์ผ๋ก ๊ฐ์ ํ๊ทธยท๋งํฌยท๊ฐ์ ์ธ๊ธฐยท๋ฐํ ์๋ ๋ฐ ๊ฐ์ ๊ณต๋ช ์ฃผํ์(`res`)๋ฅผ ๋งคํ**ํฉ๋๋ค. | |
| - ๋ํ ๋๋ณธ์ ์ฝ์ ๋ ๊ฐ์ ยท๋ฐ์ฑยท์๋ฆฌ ํ๊ทธ๋ **`_ํ๊ทธ์นธ๋ผ๊ธฐ` (Zero-Duration Scatter Compaction)**๋ฅผ ํตํด **ํ๋ ์ ์ 0(์ ๋ก๋๋ ์ด์ , Zero-Duration)**์ผ๋ก ์ฒ๋ฆฌ๋์ด, **ํ๊ทธ ์์ฒด๊ฐ ๊ธ์๋ ๊ตฐ์๋ฆฌ๋ก ๋ฐ์๋๋ ์ผ ์์ด ์ค์ง ์์ํ๊ฒ ๊ฐ์ ๊ณผ ๋ฐ์ฑ ํจ๊ณผ๋ง** ๋ณธ๋ฌธ ์์ฑ์ ์ ํ์ง๋๋ค. | |
| 3. **์๋ชจ ํ์ต์ ํตํ ์ํ ๊ธ์ ๋ฐ์ฑ *(Acoustic Character Vocalization via Direct Jamo Learning)*** | |
| - **"ใํ๋ฏผ์ ์ ํด๋ก๋ณธใ์ ๋ฐ๋ผ ์ํ(๋ฐ์ฑ ์ฃผํ์)๋ฅผ ๋ง๋ค์ด์ ์ดยท์คยท์ข ์ฑ์ ๋ฐ์ฑํ๋ ํ๊ตญ์ด ๋ฐ์์ด ๊ทธ๋๋ก ๋์จ๋ค."** | |
| - ์์ด์ IPA ์์ ๋ณํ(eSpeak ๋ฑ)์ด๋ ์๊ณก๋ ์์ด ๊ท์น์ ๊ฑฐ์น์ง ์๊ณ , **ใํ๋ฏผ์ ์ ํด๋ก๋ณธใ์ ์ ์ ์๋ฆฌ์ ๋ฐ๋ผ ํ๊ธ ์ดยท์คยท์ข ์ฑ ์๋ชจ(NFD) ์์ฒด๋ฅผ ์ํ(104์ฐจ์ ๊ณ ์ ๊ณต๋ช ์ฃผํ์)์ 1:1๋ก ์ง์ ํ์ต**์์ผ ๊ธ์ ๊ทธ๋๋ก์ ๋ง๊ณ ์์ํ ์ํ ๋ฐ์ฑ์ ๊ตฌํํ์ต๋๋ค. | |
| - *(When we construct the acoustic soundwaves / vocal resonance frequencies according to the Hunminjeongeum Haeryebon and vocalize the initial, medial, and final Jamo, authentic Korean pronunciation emerges just as written.)* | |
| 4. **์ด๊ฒฝ๋ ๊ตฌ๋ & ๋ฐ์ด์ด ๊ฑฐ์ ์์ *(Ultra-Light & Near-Zero Heat on Mobile / CPU)*** | |
| - ๋ฌด๊ฑฐ์ด ๊ฑฐ๋ ์ ๊ฒฝ๋ง ๊ธฐ์ต์ธต์ด๋ ํ๋ฐด๋ HiFi-GAN ๋ณด์ฝ๋ ๋์ , **์๋ชจยท์ฑ์ฐยท๊ฐ์ ์ฃผํ์์ ๋จ์ผ ์ ํ ํฌ์(`emb_f`, `emb_g_res`, `emb_res`) + 4-๋ถ๋์ญ MB-iSTFT-VITS(PQMF) ํฉ์ฑ ๊ตฌ์กฐ**๋ก ์๋ํฉ๋๋ค. ์ฐ์ฐ๋์ด ๊ทน๋๋ก ๊ฐ๋ฒผ์ ์ค๋งํธํฐ(๋ชจ๋ฐ์ผ)์ด๋ ์ผ๋ฐ ๋ ธํธ๋ถ CPU์์๋ **๋ฐ์ด์ด ๊ฑฐ์ ์์ด ๊ฐ๋ณ๊ณ ๋น ๋ฅด๊ฒ ์ค์๊ฐ ๊ตฌ๋**๋ฉ๋๋ค. | |
| --- | |
| ## ๊ธฐ์กด ํ์ดํผ(Piper)๋ณด๋ค ๋์๊ฐ ์ | |
| | ๊ตฌ๋ถ | ๊ธฐ์กด Piper | **Piper Plus (Epoch 880 EMA)** | | |
| |---|---|---| | |
| | **๋์๋ฆฌ(๊ฒฝ์ํ) ์ ์ด** | ๋ฌธ๋งฅ ๊ธฐ์ต์ธต์ด ๊ฒฝ์ํ๋ฅผ ๊ณผ์ ์ผ๋ฐํํ์ฌ ํ์์ด ๋์๋ฆฌ๋ก ๋ฌด๋์ง | **์ถ๋ ฅ ์ธ์ฝ๋ ๊ธฐ์ต์ธต(`self.emb`) 100% ๋ฏธํต๊ณผ(๋์ฒด) + ์ธ๊ธฐํ๋ณ๊ธฐ(์์ฌ์๋ฆฌ ๋ณดํธ 24๋ฐฐ) + ํ์ฑ ๊ฐ์ค์น ์ต์ (`0,2,4`)**๋ก ์์ฌ์๋ฆฌ ๋ณด์กด, **๋ถ๊ท์น ๋ฐ์์ ์ฌ์ ์ผ๋ก 1:1 ๋์** | | |
| | **ํ๊ตญ์ด ์ ๋ ฅ ๋ฐ ๋ฐ์ฑ** | eSpeak IPA ์์ ๋ณํ (ํ๊ตญ์ด ๋ฐ์ ๊ท์น ๋ถ์ ํยท์์ ์๊ณก) | **ํ๊ธ NFD ์๋ชจ ์ง๊ฒฐ ์ํ ๋ฐ์ฑ** โ ใํ๋ฏผ์ ์ ํด๋ก๋ณธใ ์ ์ ์๋ฆฌ๋ฅผ ๋ฐ๋ฅธ 104์ฐจ์ ์ฑ๋ ๊ณต๋ช ์ฃผํ์ ์ง์ ์ ๋ ฅ (๊ธ์ ๊ธฐ์ต ์นธ 0) | | |
| | **์ธ๊ตญ์ดยท์์ด ํ์ฅ** | ๋ฏธ์ง์ ๋๋ ์ด์ํ ์ํ๋ฒณ ์ฒ ์ ์ฝ๊ธฐ | **์๋ชจ์ยท์์์(`แซ, แ , แฝ, แ , แผ, ใ, ใ` ๋ฑ) ๊ณต๋ช ์ฃผํ์**๋ฅผ ํตํ ์์ด๊ธ ๋ง์ฐฐ์/๋ชจ์ ๋ฐ์ฑ + **๋ฏธํ์ต ์๋จ์ด ํ๊ตญ์ ๋ฐ์(์ฝฉ๊ธ๋ฆฌ์ฌ/์ํ๋ฒณ ์์ฐจ)** ์๋ ๋ณํ | | |
| | **์ถ๋ ฅ ๋์ฝ๋ & ๋ฐ์ด** | ๋ฌด๊ฑฐ์ด HiFi-GAN ์์ฑ๊ธฐ (๋ชจ๋ฐ์ผ ๋ฐ์ดยท์ง์ฐ ๋ฐ์) | **๋จ์ผ ์ ํ ๊ณต๋ช ํฌ์ + 4๋์ญ MB-iSTFT ๋์ฝ๋**(PQMF 4๋ถ๋์ญ ยท `n_fft` 16 ยท `hop` 4) โ **์ค๋งํธํฐยทCPU์์ ๋ฐ์ด์ด ๊ฑฐ์ ์์ด ์ด๊ฒฝ๋ ์ค์๊ฐ ๊ตฌ๋** | | |
| | **์ฑ์ฐ ํํ** | ํ์ ID๋ง๋ค ์ธ์ด ๊ณ ์ ์๋ฒ ๋ฉ(`emb_g`) | **์ฑ์ฐ ๊ณต๋ช ์ฃผํ์ 31๊ฐ์ง**(`spk_feat 28ร31 โ Linear 31โ512`) โ ์๋์ดยท์ฑ๋ยทํฌ๋งํธยท์์ ๊ป์ง z ์ ์๋ก ๋ชฉ์๋ฆฌ ์์ฑ | | |
| | **๊ฐ์ ํํ & ํ๊ทธ** | ์์ (๋จ์กฐ๋ก์ด ๋ญ๋ ) | **์ ์ฉ ๋ฌธ๋งฅ ๊ฐ์ ๋ชจ๋ธ(`emotion_int8.onnx`) ์๋ ๋งคํ** + **์ ๊ฒฝ๊ณต๋ช `res`(๋ ธ๋ธ 3 + ๊ผฌ๋ฆฌ 15)** + **๊ฐ์ ํ๊ทธ ์ ๋ก๋๋ ์ด์ (๊ธธ์ด 0, ์๋ฆฌ ์์ด ๊ฐ์ ๋ง ์ฃผ์ )** | | |
| | **ํธํก ๋ฐ ๋์ด ์ฝ๊ธฐ** | ์ด์ ๋ง๋ค ๊ธฐ๊ณ์ ๋จ์ ๋๋ ๊ธด ๋ฌธ์ฅ ํต์งธ ํฉ์ฑ ์ ์ต์ ๋ถ๊ดด | **์ด์ ๊ฒฝ๊ณ(`eojeol_boundary`) ์์ ์ ๊ฑฐ** + **ํ์ค ์ ์ `0.85` & ๊ฐ์ ๋น ๋ฅด๊ธฐ(`1.08/1.0/0.93`)** + **์ข ๊ฒฐํยท์ฐ๊ฒฐํ ์ด๋ฏธ ๊ฐ๋ผ์ฝ๊ธฐ(`0.01์ด` ์ด์)** | | |
| --- | |
| ## ๐๏ธ ํต์ฌ ๊ธฐ์ ์ํคํ ์ฒ (Core Technical Architecture) | |
| ### 1. 3๋ ์์ ๊ณต๋ช ์ํคํ ์ฒ (๋ฐ์๊ณต๋ช ยท ์ฑ์ฐ๊ณต๋ช ยท ๊ฐ์ ๊ณต๋ช ) & ใํ๋ฏผ์ ์ ํด๋ก๋ณธใ ์ฃผํ์ ๋งคํ | |
| > **"ใํ๋ฏผ์ ์ ํด๋ก๋ณธใ์ ๋ฐ๋ผ ์ํ(๋ฐ์ฑ ์ฃผํ์)๋ฅผ ๋ง๋ค์ด์ ์ดยท์คยท์ข ์ฑ์ ๋ฐ์ฑํ๋ ํ๊ตญ์ด ๋ฐ์์ด ๊ทธ๋๋ก ๋์จ๋ค."** | |
| > *"ใํ๋ฏผ์ ์ ํด๋ก๋ณธใ์ด ๋ฐํ ๋ฐ์ฑ๋ฒ์ ๊ทธ๋๋ก ์ฃผํ์๋ก ๋ณธ๋ ๋ง์ถ ์ํ ๋ฐ์ฑ ๋ชจ๋ธ"* | |
| > *(When we construct the acoustic soundwaves / vocal resonance frequencies according to the Hunminjeongeum Haeryebon and vocalize the initial, medial, and final Jamo, authentic Korean pronunciation emerges just as written.)* | |
| ๋ณดํต์ ์์ฑ ํฉ์ฑ ๋ชจ๋ธ์ ๊ธ์ ํ๋, ํ์ ํ๋, ๊ฐ์ ํ๋๋ง๋ค ์๋ฒ ๋ฉ ์นธ์ ๋๊ณ ๊ทธ ๊ฐ์ ๋จ์ ์๊ธฐํฉ๋๋ค. ๋ฐ๋ฉด **Piper Plus (`์์ ๊ณต๋ช ํ` Epoch 880 EMA)**๋ ๊ธ์ยท์ฑ์ฐยท๊ฐ์ ์ ๊ฐ๋ณ ๊ธฐ์ต ์นธ์ ๋ชจ๋ ๋น์ฐ๊ณ , ใํ๋ฏผ์ ์ ํด๋ก๋ณธ(่จๆฐๆญฃ้ณ ่งฃไพๆฌ)ใ์ด ๋ฐํ ๋ฐ์ฑ ๊ธฐ๊ด์ ๊ณผํ์ ์ ์ ์๋ฆฌ๋ฅผ ํ๋ ์ํฅ๋ฌผ๋ฆฌํ์ **์ฑ๋(Vocal Tract) ๊ณต๋ช ์ฃผํ์**๋ก ๊ทธ๋๋ก ์ฎ๊ธด **3๋ ์์ ๊ณต๋ช ์ํคํ ์ฒ**๋ก ์ค๊ณ๋์์ต๋๋ค. | |
| 1. **๋ฐ์๊ณต๋ช (`jamo_feat` `544ร104` โ `emb_f` `Linear(104 โ 192, bias=False)`)**: | |
| - ๊ธ์๋ณ ์๋ฒ ๋ฉ ๊ธฐ์ต ์นธ(`self.emb`)์ ์ฐ์ง ์๊ณ , ใํ๋ฏผ์ ์ ํด๋ก๋ณธใ์ ์ ์ ์๋ฆฌ๋ฅผ ๋ณธ๋ ๋ น์์์ ์ค์ธกํ **104์ฐจ์ ์๋ชจ ์ฑ์ง ํ๋ ฌ(`jamo_feat`: ์ 80์นธ์ `ํ_๋ฐํ.json` ์๋ชจ ๋ฐ์ ๊ณต๋ช ์ฃผํ์, ๋ค 24์นธ์ `ํ_ํ๊ทธ.json` ์๋ฆฌยท๋ฐ์ฑยท์์ ํ๊ทธ ์ฃผํ์)**์ ๋จ์ผ ๊ณต์ ์ ํ ๋ณํ(`emb_f`)๋ง ๊ฑฐ์ณ ํ ์คํธ ์ธ์ฝ๋๋ก ๋ค์ด๊ฐ๋๋ค(`resonance/jamo_features.json`). | |
| 2. **์ฑ์ฐ๊ณต๋ช (`spk_feat` `28ร31` โ `emb_g_res` `Linear(31 โ 512)`)**: | |
| - ์ฑ์ฐ๋ณ ์๋ฒ ๋ฉ ๊ธฐ์ต ์นธ(`emb_g`)์ ์์ ๋ง๋ค์ง ์๊ณ , ์ฑ์ฐ๋ง๋ค ๋ น์์์ ์ค์ธกํ **31๊ฐ์ง ๋ฐ์ฑ ์ฃผํ์ ๋ฒกํฐ(`z`: ์๋์ด, F1~F4, B1~B3, ์ฑ๋ ๊ธธ์ด, ์ ์ญ/ํ์ฑ, ๋ฐฐ์ฐ ํฌ๋งํธ, ๋ฐ๊ธฐ ์ค์ฌ, ๊ธฐ์ธ๊ธฐ, H1-H2, CPP, HNR, MFCC ๊ป์ง 1~12)**์ ๋จ์ผ ๊ณต์ ์ ํ ์(`Wยทz + b`)๋ง ์ ์ฉํด ์์ ์กฐ๊ฑด `g`๋ฅผ ์์ฑํฉ๋๋ค(`resonance/speaker_features.json`). | |
| 3. **๊ฐ์ ๊ณต๋ช (`tag_dummy=True` ๋น์นธ `250` + `์ฃผํ์ํ๊ทธ์นธ` + `res` 34์ฐจ์)**: | |
| - ๊ฐ์ ยท๋งํฌ ํ๊ทธ ๋ฒํธ๋ง๋ค ๋ฐ๋ก ์ธ์ฐ๋ ๊ธฐ์ต ์นธ์ ์์ ๊ณ ๋ชจ๋ ๋น ์นธ(`tag_dummy_id = 250`)์ผ๋ก ๋ณด๋ธ ๋ค, **`res` 34์ฐจ์ ์ ๊ฒฝ๊ณต๋ช ๋ฒกํฐ** โ **`res_g_dims = 3` ๊ณต๋ช ๋ ธ๋ธ**(์ฑ๋ ์ฒ๋ ยท ์ ์ญ/ํ์ฑ ยท ๋ฐฐ์ฐ ํฌ๋งํธ) + **`res_g_tail = 15` ๊ฐ์ ๊ณต๋ช ๊ผฌ๋ฆฌ**(eGeMAPS 14๊ฐ์ง ์์ง ์ฃผํ์ ๋ณํ๋ + ์ฝง์๋ฆฌ ์์ `A1-P0`) โ ๋ก๋ง ์๋ํ๊ฒ ํ์ฌ ๋ชจ๋ ์ฑ์ฐ์๊ฒ ๊ฐ์ ํํ์ด ๊ณ ๋ฅด๊ฒ ์ ์ด๋๋๋ก ์ค๊ณํ์ต๋๋ค(`emotion/emotion_resonance.json`). | |
| | ๊ณต๋ช ๊ฐ๋ | ๋ชจ๋ธ ๋ด๋ถ ๋ณํ ๊ตฌ์กฐ | ์ฐจ์(์นธ ์) | ์ค์ ํ ํ์ผ | | |
| |---|---|---|---| | |
| | **๋ฐ์๊ณต๋ช (์๋ชจ)** | `jamo_feat` (`544ร104`) โ `emb_f` (`Linear 104โ192, bias=False`) โ `self.emb` 100% ๋์ฒด | 104 (๋ฐ์ 80 + ํ๊ทธ 24) | `resonance/jamo_features.json` | | |
| | **์ฑ์ฐ๊ณต๋ช (ํ์)** | `spk_feat` (`28ร31`) โ `emb_g_res` (`Linear 31โ512`) โ ํ์ ์๋ฒ ๋ฉ `emb_g` ์์ | 31 | `resonance/speaker_features.json` | | |
| | **๊ฐ์ ๊ณต๋ช (๊ฐ์ )** | `res` (`[b, 34]`): ๊ณต๋ช ๋ ธ๋ธ `3` (`[0:3]`) + ๊ฐ์ ๊ผฌ๋ฆฌ `15` (`[19:34]`, eGeMAPS 14 + ์ฝง์๋ฆฌ `A1-P0`) | 3 + 15 (`res` 34) | `emotion/emotion_resonance.json` | | |
| --- | |
| ### 1-1. ใํ๋ฏผ์ ์ ํด๋ก๋ณธใ ์ํ ๋ฐ์ฑ์ ์ฝ๋์ ยท์ํ์ ์ค์ฆ (`ํ_๋ฐํ.json` ยท `ํ์ต์ฃผํ์ํ๋ง๋ค๊ธฐ.py` ยท `ํด๋ก๋ณธ๋ฐ์นจ.py`) | |
| ๋น์ ๊ฐ ์๋๋ผ **์ค์ ์ ๊ฒฝ๋ง์ ์ํ์ ์ฐ์ฐ ์์ฒด๊ฐ ใํ๋ฏผ์ ์ ํด๋ก๋ณธใ์ ์๋ฆฌ ๊ทธ๋๋ก๋ง ๋์๊ฐ๋๋ก** ๊ตฌํ๋์ด ์์ต๋๋ค. | |
| #### โ ๊ธ์ ์ด๋ฆํ(0/1 ๊ธฐํธ ์นธ) 49๊ฐ ์ ๋ `0` ์ ๊ฑฐ โ ์ค์ง 31๊ฐ ๋ฌผ๋ฆฌ์ ์ํ ์ฃผํ์๋ง ์๋ | |
| - `resonance/jamo_features.json`(`ํ_๋ฐํ.json` 80์ด + ํ๊ทธ ์ฃผํ์ 24์ด = 104์ด)์ ๊ธฐ๋ณธ 80๊ฐ ์ด์ ๊ฒ์ฌํ๋ฉด, ์ด๊ธฐ ์ค๊ณ์ ์๋ `๊ธ์ใฑ~ใ `, `์๋ฆฌ:์ด๊ธ๋/ํ/์ ์ `, `๋ฐฉ์:๋งํ/๊ฐ๋ฆผ`, `์ธ๊ธฐ:์์ฌ/๊ฑฐ์ผ/๋` ๋ฑ **๊ธฐํธ ํ์(One-Hot) ์นธ 49๊ฐ๋ ์ ๋ `0.0`**์ผ๋ก ๋น์์ ธ ์์ต๋๋ค (`assert all(v == 0 ... if ์ด๋ฆ[i] not in ์ฐ)`). | |
| - ๋์์ `models.py`(`jamo_feature_mode = "๋์ฒด"`)์์ ๋ชจ๋ ์๋ชจ ํ ํฐ์ ๊ฐ๋ณ ๊ธ์ ์๋ฒ ๋ฉ ๊ธฐ์ต ์นธ(`self.emb(x)`)์ **100% ๋ฒ๋ฆฌ๊ณ **, ์ค์ง `self.emb_f(jamo_feat[x])`๋ง ์ธ์ฝ๋๋ก ๋ค์ด๊ฐ๋๋ค. | |
| - ์ฆ, ์ ๊ฒฝ๋ง์ ์ง๊ธ ๋ค์ด์จ ๊ธ์๊ฐ `'ใฑ'`์ธ์ง `'ใ '`์ธ์ง ๋ฒํธ๋ ๊ธฐํธ๋ก๋ ์ ํ ์ ์ ์๊ณ , **์ค์ง 31๊ฐ์ ๋ฌผ๋ฆฌ์ ์ํ ์ฃผํ์ ์์น๋ง ๋ณด๊ณ ์๋ฆฌ๋ฅผ ํฉ์ฑํฉ๋๋ค.** | |
| #### โก ์ด์ฑยท์ค์ฑยท์ข ์ฑ 31๊ฐ ์ฃผํ์ ์นธ๊ณผ ใํ๋ฏผ์ ์ ํด๋ก๋ณธใ ๊ตฌ์ ์ 1:1 ์ง๊ฒฐ | |
| 1. **์ด์ฑ 16์นธ โ ํด๋ก๋ณธ `ๅ่ฒๆ็ผๅไน็พฉ` (์ด์ฑ์ ์๋ฆฌ๋ฅผ ์ผ์ผํจ๋ค) & ์ค์(์ยท์คยท์ยท์นยทํ)ยท๊ฐํ** | |
| - ์กฐ์ ์์น(์ ์ ์๋ฆฌ `ใ /ใ `์ ์ ์ญ์ ํผ์ง๊ณ ์ด๊ธ๋์๋ฆฌ `ใฑ/ใ `์ ์ค์ญ์ ๋ชฐ๋ฆฌ๋ `์ด์ฑํฐ์ง์ ์ค๋นยท์ค๊ณ ๋น`, `์ด์ฑํฐ์งkHz`, `์ด์ฑํฐ์ง๋ด์ฐ๋ฆฌ/ํผ์งkHz`, `์ด์ฑF2/F3ํจ`)์ ๊ฐํ(์ ์ฒญยท์ฐจ์ฒญยท์ ํ์ ๋ฐ๋ฅธ `์ด์ฑ์จ`, `์ด์ฑ์จ์๋ฆฌkHz/๋ด์ฐ๋ฆฌ/ํผ์ง/์ ์ค๋น/์ค๊ณ ๋น`, `์ด์ฑ์ธ๊ธฐ`, `์ด์ฑ๋ค์๋์ด`, `์ด์ฑ์ธ๋ฆผkHz`) ์ค์ธก์น๋ง ์ ๋ ฅ๋ฉ๋๋ค. | |
| - ํนํ ์ด์ฑ `ใ `์ `0`์ผ๋ก ๋น์ ์ ๋ ๊ธฐ์ต ์นธ(`self.emb`)์ผ๋ก ๋น ์ ธ ๋ชจ์ ์ฐ์ ๋ฐ์(`์์โ์`, `์ผ์โ์`)์ด ๋ญ๊ฐ์ง๋ ํ์์, ํด๋ก๋ณธ ์ ์ํด ์๋ฌธ **`ใใ ใๅ้ณใๅฆๆฌฒๅญๅ็ผ่ฒใ`**(๋ชฉ์ฒญ์์ ์๋ฆฌ๋ฅผ ์ฌ๋ ํ์) ๊ทธ๋๋ก ์ฝ๋์ ์ ์ฉํด ์ค์ ๋ชฉ์ฒญ ๋ฐ์ฑ ์ฃผํ์๋ฅผ ๋ฃ์ด ํด๊ฒฐํ์ต๋๋ค. | |
| 2. **์ค์ฑ 7์นธ โ ์ฒยท์งยท์ธ ๋จ๋ชจ์๊ณผ ํฉ์ฉ(์ด์ค๋ชจ์)์ ํฌ๋จผํธ ์ถ๋ฐยท๋์ฐฉ ๊ถค์ ** | |
| - ๋ชจ์ ์์ 10% ์ง์ (`๊ณต๋ช F1ยทF2ยทF3์ถ๋ฐ`)๊ณผ 75% ์ง์ (`๊ณต๋ช F1ยทF2ยทF3๋์ฐฉ`), `๋ชจ์๊ธธ์ด` 7์นธ๋ง์ผ๋ก ๋จ๋ชจ์๊ณผ ํฉ์ฉ ์ด์ค๋ชจ์(`ใ , ใ , ใ , ใ , ใ ข` ๋ฑ)์ ์ฑ๋ ๊ณต๋ช ์ด๋ ๊ถค์ ์ ๊ทธ๋๋ก ํํํฉ๋๋ค. | |
| 3. **์ข ์ฑ(๋ฐ์นจ) 8์นธ + ์ฐ์ ๋์ด๊ฐ 16์นธ โ ํด๋ก๋ณธ ์ข ์ฑํดยทํฉ์ํด ์๋ฌธ ๊ตฌํ์ผ๋ก ๋ฐ์นจ ๋ฏธ๋ถํ ํด๊ฒฐ (`ํด๋ก๋ณธ๋ฐ์นจ.py`)** | |
| - ์ํญ 632 ๋น์ ๋จ์ ๋ชจ์ ์ค์๊ฐ ๋น์จ์ ๋ฃ์์ ๋๋ `๊ฐยท๊ฐยท๊ฐ`์ด ์๋ฆฌ๋ก ๊ฐ๋ฆฌ์ง ์๊ณ ๋ชจ๋ `์นธ`์ผ๋ก ๋ฐ์๋์์ผ๋(๋ 40% ์ฐจ์ด 3~8dB), ํด๋ก๋ณธ์ 5๊ฐ์ง ์๋ฆฌ๋ฅผ ์ฝ๋๋ก ๊ตฌํํด ๋ฃ์ ๋ฐ์นจ ๋ฐ์ฑ์ด ์ ํํ ๋ถ๋ฆฌ๋์์ต๋๋ค(์ ๋ ฅ ๊ฑฐ๋ฆฌ `ใดโใ ` 0.98โ1.95, `ใฑโใ ` 1.45โ3.72): | |
| - **`็ต่ฒๅพฉ็จๅ่ฒ` & `ใ่ใฑ็ฒๅฐ ยท ่ไนใดใท ยท ่ฃไนใ ใ ` (์ด์ฑ์ ๋ค์ ์ฐ๋ฉฐ ์ฝง์๋ฆฌ์ ๋งํ์๋ฆฌ๋ ์กฐ์ ์๋ฆฌ๊ฐ ๊ฐ๋ค)**: ์ด๊ธ๋(`ใฑยทใ` `1,503Hz`), ํ(`ใดยทใท` `1,875Hz`, `ใน` `1,813Hz`), ์ ์ (`ใ ยทใ ` `559Hz`) ์ง์ ๋ฌถ์ด ๊ตฌํ ๋ก์ปค์ค ์๋ฆฌ ์ฃผํ์(`z2์ง`, `z3์ง`)๋ฅผ `๋ฐ์นจF2ํจยทF3ํจ`์ ์ ๋ ฅ. | |
| - **`ๅ ซๅญๅฏ่ถณ็จ` (8์ข ์ฑ ๊ฐ์กฑ์ฉ)**: `ใฒยทใ โใฑ`, `ใ ยทใ ยทใ ยทใ ยทใ โใท`, `ใ โใ `์ผ๋ก 8์ข ์ฑ ๋ํ ์๋ฆฌ ์ฃผํ์๋ฅผ ๋งคํ(`ใ `์ ํ์์ด๋ผ ์ ์ ์๋ฆฌ ํจ `0`). | |
| - **`็ต่ฒๆๆญขๅฎไน็พฉ` & `ไธๆทธไธๆฟไนๅญ ๅ ถ่ฒไธๅฒ(่็ทฉ) / ๅ จๆทธโฆๅ ถ่ฒ็ฒๅฒ(ไฟๆฅ)`**: ๋ฐ์นจ ์นธ ์์ฒด์๋ ํฐ์งยท์จ์ ๋์ง ์๊ณ , ๋ถ์ฒญ๋ถํ(`ใดยทในยทใ ยทใ `)์๋ง ์ธ๋ฆผ(`๋ฐ์นจ์ธ๋ฆผkHz`)๊ณผ ๋น๊ฐ ๋ฐ๊ณต๋ช (`๋ฐ์นจ์ฝง์๋ฆฌ๊ณจkHz`: `ใด 1.38 / ใ 1.30 / ใ 1.73kHz`)์ ๋ถ์ฌํ๋ฉฐ ๋งํ ๋ฐ์นจ(`๋ซํ๋น > 0.5`)์ ์ธ๋ฆผ์ `0`์ผ๋ก ์ค์ . | |
| - **`็ต่ฒไบๅญไธๅญๅ็จ ่ชๅทฆ่ๅณ` (ํฉ์ํด)**: ๊ฒน๋ฐ์นจ(`ใณ, ใต, ใถ, ใบ, ใป, ใผ, ใฝ, ใพ, ใฟ, ใ , ใ `)์ ์ผ์ชฝ ์ฒซ ์์ ์๋ฆฌ(`๋ฐ์นจF2ํจยทF3ํจ`)๋ก ๋ค์ด๊ฐ ์ค๋ฅธ์ชฝ ๋์งธ ์์ ์๋ฆฌ(`๋ฐ์นจ๋์งธF2ํจยทF3ํจ`)๋ก ๋ซํ๊ณ , ๊ธธ์ด๋ ๋ ์๋ฆฌ์ ํฉ์ผ๋ก ๊ณ์ฐํ๋ฉฐ, ๋ชจ์ ์์์ ๋ค์ ์์ ์ฒซ์๋ฆฌ๋ก ๋ฐ๋ํ๋ ์ฐ์(๋์ด๊ฐ)์ ๋ฐ์นจ ์ค์ ์ด์ฑ 16์นธ์ ํจ๊ป ์ค์ด ๋ชจ๋ธ์ด ๋ฌธ๋งฅ์ ๋ฐ๋ผ ์ ํํ๊ฒ ํจ. | |
| #### โข ์๊ธ์(์์ด ๋ง์ฐฐ์)๊น์ง ๋์ผํ ์ฃผํ์ ์ ํ๋๋ก ๋ฐ์ฑ | |
| - ํ๊ตญ์ด ๋ณธ๋ฌธ ๋ น์์ ์๋ ์์์ `แ [f]`, `แซ[v]`, `แ [z]`, `แผ[ฮธ]`, `แฝ[รฐ]`(๋ฒํธ `384~388`) ์ญ์ ๊ธ์ ๊ธฐ์ต ์นธ ์์ด ์ด์ฑ 16์นธ์ ์์ด ๋ง์ฐฐ์ ์ค์ธก ์ฃผํ์๋ง ์ฑ์ ๋ฃ์์ ๋ ๋์ผํ `emb_f` ์ ํ๋๋ก ๋ฐ์ฑ๋ฉ๋๋ค. | |
| --- | |
| ### 2. ์ ๋์๋ฆฌ๋ฅผ ํผํด๊ฐ ์ ์์๋๊ฐ? โ ์๋ชจ ๊ธฐ์ต์ธต(`self.emb`) 100% ์ฐํ & ์ธ๊ธฐํ๋ณ๊ธฐ 24๋ฐฐ ๋ณดํธ & ๋ถ๊ท์น ๋ฐ์ ์ฌ์ ๋์ | |
| ๊ธฐ์กด ํ๊ตญ์ด ์ ๊ฒฝ๋ง TTS๋ค์ด ์์ฌ์๋ฆฌ๋ฅผ ๊ณผ๋ํ๊ฒ ๋์๋ฆฌ๋ก ๋ฐ์ํ๋ ๊ทผ๋ณธ ์์ธ์ **๊ธ์๋ณ ์๋ฒ ๋ฉ ๊ธฐ์ต์ธต(`self.emb`)๊ณผ ๋ฌธ๋งฅ ๊ธฐ์ต์ ๊ฒฝ์ํ ๊ณผ์ ์ผ๋ฐํ**์ ์์ต๋๋ค. ํ์ต ๋ฐ์ดํฐ์ `์ธ๊ถ[์ธ๊ฟ]`, `์๊ฐ๋ฝ[์๊น๋ฝ]`์ฒ๋ผ ํ๊ธฐ๋ ์์ฌ์๋ฆฌ(`ใฑ`)์ธ๋ฐ ๋ น์์ ๋์๋ฆฌ(`[ใฒ]`)์ธ ๋จ์ด๊ฐ ๋ค์ด ์์ผ๋ฉด, ๊ธฐ์ต์ธต์ ํต๊ณผํ๋ ์ ๊ฒฝ๋ง์ *"์ธ๋ฆผ ๋ฐ์นจ ๋ค์ ํ์์ ๋์๋ฆฌ๋ก ์ฝ๋๋ค"*๋ ๋ฒ๋ฆ์ ์ธ์ `์๊ถ`๊น์ง `[์๊ฟ]`์ผ๋ก ์ฝ๊ณ ๋ฌธ์ฅ ๊ณณ๊ณณ์ ํ์์ ๊ฒฝ์์ผ๋ก ์ค์ผ์ํต๋๋ค. | |
| **Piper Plus (`์์ ๊ณต๋ช ํ` Epoch 880 EMA)**๋ ์ด๋ฅผ ๋ค์ 4๋จ๊ณ ์ํคํ ์ฒ์ ์ญํ ๋ถ๋ด์ผ๋ก ์๋ฒฝํ ํด๊ฒฐํ์ต๋๋ค: | |
| 1. **์ถ๋ ฅยทํ ์คํธ ์ธ์ฝ๋์์ '๋์๋ฆฌํ๋ ์๋ชจ ๊ธฐ์ต์ธต(`self.emb`)' 100% ๋ฏธํต๊ณผ(๋์ฒด) ์ค๊ณ (`jamo_feature_mode = "๋์ฒด"`)**: | |
| - `models.py` (`TextEncoder.forward` 297~302ํ, `_๋ฐ์ฑ์์ง` 1453~1456ํ): | |
| ```python | |
| ๋ชซ = self.emb_f(F.embedding(x, self.jamo_feat)) # [544, 104] ์ฃผํ์ ์ฑ์ง ํ๋ ฌ -> Linear(104, 192, bias=False) | |
| x = torch.where(์๋ชจ์นธ.unsqueeze(-1), ๋ชซ, x) # ์๋ชจ ์นธ์ ๊ธ์๋ณ ๊ธฐ์ต ์นธ(self.emb)์ 100% ๋ฒ๋ฆฌ๊ณ ์ฃผํ์ ์ฑ์ง ๋ชซ์ผ๋ก ๋์ฒด! | |
| ``` | |
| - ๋ชจ๋ ํ๊ธ ์๋ชจ(ํ๋ ์๋ชจ + ์๋ชจ์ยท์์์)๋ ๋์๋ฆฌ ๋ฒ๋ฆ์ด ํ์ต๋ **๊ธ์๋ณ ์๋ฒ ๋ฉ ๊ธฐ์ต ์นธ(`self.emb`)์ ์์ ํต๊ณผํ์ง ์๊ณ **, ์ค์ง ใํ๋ฏผ์ ์ ํด๋ก๋ณธใ์ ๋ณธ๋ ์ค์ธกํ **104์ฐจ์ ๋ฐ์ฑ ์ฃผํ์ ํ๋ ฌ(`jamo_feat`)**์ ๋จ์ผ ๊ณต์ ์ ํ ๋ณํ(`emb_f`)๋ง ๊ฑฐ์ณ ์ธ์ฝ๋๋ก ์ง๊ฒฐ๋ฉ๋๋ค. | |
| - ๋ํ `_๋ฌธ๋งฅ์ฑ์ง(x)`(1427~1435ํ)์์ ์ธ์ฝ๋ ์๋จ์ ์ธ์์ ๋ฐ์ ๊ท์น์ ์์ ํ ๋๊ณ (*"์ ๋ ฅ ์ธ์ฝ๋๋ ์๋ค ๊ธ์๋ฅผ ๋ณด๊ณ ์ฑ์ง์ ๋ฐ๊พธ์ง ์๋๋ค โ ๊ทธ๋ฅ ์ฃผํ์๋ก ์ฝ๋๋ค"*), ์ํญ 437์์ ๋์๋ฆฌ๊ฐ ๊ตณ์ด์ง ๊ธฐ์กด ํ ์คํธ ์ธ์ฝ๋(`enc_p.*` 112๊ฐ ํค)๋ฅผ ํต์งธ๋ก ๋น์ด ๋ค **๋ฐ์๊ณต๋ช ยท์ฑ์ฐ๊ณต๋ช ยท๊ฐ์ ๊ณต๋ช ์ธ ๊ฐ์ง๋ง ๊ธฐ์ตํ๋ '์์ ๊ณต๋ช ํ'**์ผ๋ก ์ํญ 880๊น์ง ์ฌํ์ตํ์ต๋๋ค. (์์ธ๋ฌ `TextEncoder` ๋ด๋ถ์๋ ๋ฐ์ ์ฌ์ ๋ถํฌ `m_p, logs_p`๊ฐ ๋ฌธ๋งฅ ์ธ์ฝ๋๋ฅผ ์ฐํํ๋ `pron_context_free` / `_๋ฐ์์ถ๊ตฌ` ๋ชจ๋๋ ํจ๊ป ๊ตฌํ๋์ด ์์ต๋๋ค.) | |
| 2. **ํ๊ตญ์ด ์ธ๊ธฐํ๋ณ๊ธฐ(`tense_clf=True`) & ์์ฌ์๋ฆฌ 24๋ฐฐ ๋ณดํธ ๊ฐ์ค์น(`tense_plain_weight=24.0`)**: | |
| - ํ์ต ์ ์์ฑ๋ ์์ฑ ํํ์์ ์์ฌ์๋ฆฌยท๋์๋ฆฌยท๊ฑฐ์ผ์๋ฆฌ๋ฅผ ํ๋ณํ๋ **์ธ๊ธฐํ๋ณ๊ธฐ(`tense_clf=True`, `c_tense_gen=8.0`)**๋ฅผ ๋๊ณ , ํนํ **์์ฌ์๋ฆฌ ๋ณดํธ ๊ฐ์ค์น(`tense_plain_weight=24.0`)**๋ฅผ ๋ถ์ฌํ์ฌ ๋ชจ๋ธ์ด ์์ฌ์๋ฆฌ ๊ธ์๋ฅผ ์์๋ก ๋์๋ฆฌ๋ก ๋ฐ์ฑํ๋ฉด **24๋ฐฐ์ ๊ฐ๋ ฅํ ๋ฒ์ (Loss)**์ ๋ฐ๋๋ก ์ค๊ณํ์ต๋๋ค. | |
| 3. **๊ฐ์ด(ํ์ฑ) ๊ณต๋ช ๋์ญ ๊ณผ์ ๊ฐ์ค์น ์ต์ (`res_band_weights = "0,2,4"`)**: | |
| - ์ ์ญ(ํ์ฑ) ๋์ญ ๊ฐ์ค์น๋ฅผ `0`์ผ๋ก ์ต์ (`res_band_weights='0,2,4'`)ํ์ฌ, ์ ์ญ ์๋์ง๊ฐ ๊ณผ๋ํ๊ฒ ๋ถํ์ด ์์ฌ์๋ฆฌ๊ฐ ๋ฌด๊ฒ๊ณ ๋ฑ๋ฑํ ๋์๋ฆฌ์ฒ๋ผ ๋ค๋ฆฌ๋ ํ์์ ์์ฒ ์ฐจ๋จํ์ต๋๋ค. | |
| 4. **๋ถ๊ท์น ๋ฐ์์ ๋ฐ์์ฌ์ (Dictionary)์ผ๋ก 1:1 ์ ๋ฐ ๋์**: | |
| - ๊ธฐ์ต์ธต์ ์ฐํํ์ฌ ๋ชจ๋ธ์ด ๊ธ์ ๊ทธ๋๋ก์ ๋ง์ ์์ฌ์๋ฆฌ๋ฅผ 100% ๋ฐ์ฑํ๊ฒ ๋ง๋ ๋์ , ์ค์ ํ๊ตญ์ด์์ ์๋ฆฌ๊ฐ ๋ฐ๋์ด์ผ ํ๋ **๋ถ๊ท์น ๋ฐ์(์์ธ์ ๊ฒฝ์ํ, ํ์์ด ๋์๋ฆฌ `์ธ๊ถโ์ธ๊ฟ`, ์ฌ์ด์์ท `์๊ฐ๋ฝโ์๊น๋ฝ`, ์ฐ์ยท`ใด/ใน ์ฒจ๊ฐ` ๋ฑ)**์ ์ ๊ฒฝ๋ง์ ๋ถํ์คํ ์ถ์ธก์ ๋งก๊ธฐ์ง ์๊ณ ์ถ๋ก ์๋จ์ **๋ฐ์์ฌ์ (๋ถ๊ท์น๋จ์ด์ฌ์ )**์ผ๋ก 1:1 ์ ๋ฐ ๋์ํ๋๋ก ์ญํ ๋ถ๋ด์ ์์ฑํ์ต๋๋ค. | |
| --- | |
| ### 3. ๋ฌธ๋งฅ(Context) ๊ธฐ๋ฐ ์ ์ฉ ๊ฐ์ ๋ชจ๋ธ ์๋ ๋งคํ & ์ ๋ก๋๋ ์ด์ (Zero-Duration) ๊ฐ์ ํ๊ทธ | |
| 1. **๋ฌธ๋งฅ์ ์ฝ์ด ์๋์ผ๋ก ๊ฐ์ ํ๊ทธยท๊ณต๋ช ์ฃผํ์๋ฅผ ๋งคํํ๋ ์ ์ฉ ๊ฐ์ ๋ชจ๋ธ (`emotion/emotion_int8.onnx`)**: | |
| - ์์ฐ์ค๋ฌ์ด ๊ฐ์ ํํ์ ์ ๋ํ๊ธฐ ์ํด, ๋ฌธ์ฅ์ ์๋ค ๋งฅ๋ฝ(Context)๊ณผ ํํ์ ๋ถ์ํ๋ **์ ์ฉ ํ๊ตญ์ด ๊ฐ์ ๋ชจ๋ธ(`emotion/emotion_int8.onnx`, KoELECTRA-small ๊ธฐ๋ฐ ๋ค์ค ๋จธ๋ฆฌ ๋ถ๋ฅ๊ธฐ)**์ ์ง์ ํ์ตํ์ฌ ๊ฒฐํฉํ์ต๋๋ค. | |
| - ์ฌ์ฉ์๊ฐ ๋๋ณธ์ ์ผ์ผ์ด ๊ฐ์ ํ๊ทธ๋ฅผ ๋ฌ์ง ์์๋, ๊ฐ์ ๋ชจ๋ธ์ด ์ ๋ ฅ ๋ฌธ์ฅ์ ๋ฌธ๋งฅ์ ๋ถ์ํ์ฌ **๊ฐ์ ๊ฐ๋(32์ข ๊ฐ์ ยท๋งํฌ ํ๊ทธ ์ฒด๊ณ), ๊ฐ์ ์ธ๊ธฐ(ํฌ๊ธฐ), ๋งํ๊ธฐ ๋น ๋ฅด๊ธฐ(`๋๋ฆฌ๊ฒ 1.08 / ๋ณดํต 1.0 / ๋น ๋ฅด๊ฒ 0.93`), ์จ๊ฒฐยท์๋์ดยท์๋์ง**๋ฅผ ์๋์ผ๋ก ํ๋ณํ๊ณ , ์ด๋ฅผ `emotion/emotion_resonance.json`์ **34์ฐจ์ ์ ๊ฒฝ๊ณต๋ช ๋ฒกํฐ(`res`)**๋ก ์ฆ์ ๋งคํํ์ฌ ์์ํ ๊ฐ์ ์ฐ๊ธฐ๋ฅผ ์ด๋์ด๋ ๋๋ค. | |
| 2. **๊ฐ์ ํ๊ทธ ์ ๋ก๋๋ ์ด์ (`_ํ๊ทธ์นธ๋ผ๊ธฐ` Zero-Duration Scatter Compaction) โ ์๋ฆฌ๋ ์ ๋ด๊ณ ๊ฐ์ ๋ง ์ฃผ์ **: | |
| - ๋ฌธ์ฅ ๋จธ๋ฆฌ๋ ์ค๊ฐ์ ์ฝ์ ๋ ๊ฐ์ ยท๋ฐ์ฑยท์๋ฆฌ ํ๊ทธ๋ ํ ์คํธ ์ธ์ฝ๋ ๋ด๋ถ์์ ์ฃผ๋ณ ๋ณธ๋ฌธ ์๋ชจ์ ๊ฐ์ ์ฃผํ์์ ์ต์ยท๋ฐ์ฑ ํจ๊ณผ๋ง ์ ํ๋๋ค. | |
| - ์ดํ ๊ธธ์ด ์์ธก๊ธฐ(Duration Predictor)์ ๋์ฝ๋๋ก ๋์ด๊ฐ๊ธฐ ์ง์ , ๋ชจ๋ธ ๋ด๋ถ์ **`_ํ๊ทธ์นธ๋ผ๊ธฐ`(Zero-Duration Scatter Compaction)**๊ฐ ํ๊ทธ ์นธ์ 100% ๋ผ์ด๋ด์ด **ํ๋ ์ ์ 0(์ ๋ก๋๋ ์ด์ , Zero-Duration)**์ผ๋ก ์๊ฑฐํฉ๋๋ค. | |
| - ๋๋ถ์ **ํ๊ทธ ๊ธฐํธ ์์ฒด๊ฐ ๊ธ์ ์๋ฆฌ๋ ๊ธฐ๊ดดํ ๊ตฐ์๋ฆฌ๋ก ๋ฐ์๋๋ ์ผ์ด ์ ๋ ์์ผ๋ฉฐ, ์ค์ง ์์ํ๊ฒ ๊ฐ์ ๊ณผ ๋ฐ์ฑ ๊ณต๋ช ํจ๊ณผ๋ง** ์์ฑ์ ๋จ์ต๋๋ค. | |
| --- | |
| ### 4. ์๋ชจ์ยท์์์(์ํ๊ธ)์ ํ์ฉํ ์ธ๊ตญ์ด ๋ฐ์ ๊ตฌํ & ๋ฏธํ์ต ์๋จ์ด ํ๊ตญ์ ๋ฐ์(์ฝฉ๊ธ๋ฆฌ์ฌ) ์ฒ๋ฆฌ | |
| 1. **ํ๋ฏผ์ ์ ์์์ยท์๋ชจ์ ๊ณต๋ช ์ฃผํ์๋ฅผ ํตํ ์์ดยท์ธ๊ตญ์ด ์์ ํ์ฅ**: | |
| - ๊ฐ๋ณ ๊ธ์ ๊ธฐ์ต ์นธ(`self.emb`)์ ์ฐ์ง ์๊ณ `jamo_feat`(`544ร104`) ๊ณต๋ช ์ฃผํ์ ํ๋ ฌ๊ณผ ๊ณต์ ์ ํ ๋ณํ(`emb_f`)๋ง์ผ๋ก ์๋ฆฌ๋ฅผ ์ง๊ธฐ ๋๋ฌธ์, ํ์ต ์ฝํผ์ค์ ๋ฑ์ฅ ๋น๋๊ฐ ์ ๊ฑฐ๋ ๋ฏธํ์ต๋ **ํ๋ฏผ์ ์์ ์์์ โ ์๊ฒฝ์ ํผ์ `แ `(U+1157, ID 384 `[f]`), ์๊ฒฝ์ ๋น์ `แซ`/`ใ ธ`(U+112B, ID 385 `[v]`), ๋ฐ์น์ `แ `/`ใ ฟ`(U+1140, ID 386 `[z]`), ์น๋์ ์์ท `แผ`(U+113C, ID 387 `[ฮธ]`), ์ ์น์ ์์ท `แฝ`(U+113D, ID 388 `[รฐ]`), ์ฌ๋ฆฐํ์ `ใ`, ์์ด์ `ใ` โ ๋ฐ ์๋ชจ์(์๋์ `ใ`, `ใ` ๋ฑ)**๋ ์ฃผํ์ ๊ฐ๋ง์ผ๋ก ์ฆ์ ์ ํํ ์ํ ๊ธ์ ๋ฐ์ฑ์ด ๊ฐ๋ฅํฉ๋๋ค. | |
| - ์ด๋ฅผ ํตํด ํ๋ ํ๊ธ ํ๊ธฐ๋ง์ผ๋ก๋ ๊ตฌ๋ณํ ์ ์๋ ์์ดยท์ธ๊ตญ์ด์ ์ ์ฑ ๋ง์ฐฐ์(`[v] โ แซ`, `[z] โ แ `, `[รฐ] โ แฝ`)๊ณผ ๋ฏธ์ธํ ๋ชจ์ ์๊ฐ๋ฅผ ์์์ยท์๋ชจ์์ ๊ณต๋ช ์ฃผํ์ ์กฐํฉ์ผ๋ก ์์ฐ์ค๋ฝ๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค. | |
| 2. **๋ฏธํ์ต ์๋จ์ด(OOV English) ๋ฐ ์ฝ์ด์ ์์ฐ์ค๋ฌ์ด ํ๊ตญ์ ๋ฐ์(์ฝฉ๊ธ๋ฆฌ์ฌ / ์ํ๋ฒณ-ํ๊ธ ์์ฐจ) ์ฒ๋ฆฌ**: | |
| - ๋ณ๋์ ์์ด ์์ด๋ฏผ ์์ฑ ๋ฐ์ดํฐ์ ์ผ๋ก ์ง์ ํ์ตํ์ง ์์ ๋ฏธํ์ต ์๋จ์ด๋ ์๋ฌธ ์ฝ์ด๊ฐ ์ ๋ ฅ๋๋๋ผ๋ ๊นจ์ง๊ฑฐ๋ ์นจ๋ฌตํ์ง ์์ต๋๋ค. | |
| - CMU ๋ฐ์๊ธฐํธ(ARPAbet)์ ๊ตญ๋ฆฝ๊ตญ์ด์ ์ธ๋์ด ํ๊ธฐ๋ฒ ๋ฐ ์๊ธ์ ์ธ๋ฆผ ๊ณต๋ช (`แซยทแ ยทแฝ`)์ ๊ฒฐํฉํ **ํ๊ตญ์ ์์ด ๋ฐ์(์ฝฉ๊ธ๋ฆฌ์ฌ) ๋ณํ**์ ํตํด `coffee โ ์นดํผ`, `thank โ ์ํฌ`, `very โ แซแ ฆ๋ฆฌ(๋ฒ ๋ฆฌ)`, `music โ ๋ฎคแ แ ตแจ(๋ฎค์ง)`์ฒ๋ผ ํ๊ตญ์ด ํ์์๊ฒ ๊ฐ์ฅ ์์ฐ์ค๋ฌ์ด ๋ฐ์์ผ๋ก ๋ฐ๊พธ์ด ์ฝ์ผ๋ฉฐ, ๋ฏธ๋ฑ๋ก ์๋จ์ด๋ ๋๋ฌธ์ ์ฝ์ด(`SNS โ ์์ค์์์ค`, `TV โ ํฐแซใ ฃ`, `OK โ ์ค์ผ์ด`)๋ ์ํ๋ฒณ ํ๊ธ ์์ฐจ ๊ท์น์ผ๋ก ๋งค๋๋ฝ๊ฒ ๋ฐ์ฑํฉ๋๋ค. | |
| --- | |
| ### 5. ์ด๊ฒฝ๋ยท๋ฌด๋ฐ์ด ๊ตฌ๋ & Epoch 880 EMA์ ํธํกยท์๋ยท๊ฐ๋ผ์ฝ๊ธฐ ํ์ | |
| 1. **์ด๊ฒฝ๋ ๊ตฌ๋ & ์ค๋งํธํฐยทCPU ๋ฌด๋ฐ์ด ์ค๊ณ (Ultra-Light & Near-Zero Heat)**: | |
| - ๊ฑฐ๋ํ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ์ง ์๋ฒ ๋ฉ ๊ธฐ์ต์ธต์ด๋ ์ฐ์ฐ ์ง์ฝ์ ์ธ ํ๋ฐด๋ HiFi-GAN ๋ณด์ฝ๋๋ฅผ ์ ๊ฑฐํ๊ณ , **์๋ชจยท์ฑ์ฐยท๊ฐ์ ์ฃผํ์์ ๊ฐ๋ฒผ์ด ๋จ์ผ ์ ํ ํฌ์(`emb_f`, `emb_g_res`, `emb_res`) + 4-๋ถ๋์ญ MB-iSTFT-VITS(PQMF ์ญํธ๋ฆฌ์ ๋ณํ) ๋์ฝ๋**๋ก ํํ์ ์ง์ ํฉ์ฑํฉ๋๋ค. ๋ชจ๋ฐ์ผ ๊ธฐ๊ธฐ(์ค๋งํธํฐ)๋ ์ ์ ๋ ฅ CPU์์๋ ์ฅ์๊ฐ ์ฐ์ ๋ญ๋ ์ **๋ฐ์ด์ด ๊ฑฐ์ ์์ด ๋น ๋ฅด๊ณ ์พ์ ํ๊ฒ ๊ตฌ๋**๋ฉ๋๋ค. | |
| 2. **์ด์ ๊ฒฝ๊ณ(`eojeol_boundary`) ์์ ์ ๊ฑฐ**: | |
| - ๋์ด์ฐ๊ธฐ(์ด์ )๋ง๋ค ๋์ ํฉ(CumSum)์ผ๋ก ์ด์ ๋ฒํธ๋ฅผ ์ธ์ด ์ธ์ฝ๋ ์ดํ ์ ์ ๊ฐ๋๋ ์ด์ ๊ฒฝ๊ณ ๊ธฐ๋ฅ์ ๋ชจ๋ธ์์ ์์ ํ ์ ๊ฑฐํ์ต๋๋ค. ์ด์ ๋ง๋ค ๊ธฐ๊ณ์ ์ผ๋ก ๋๋ ๋์ด ์ฝ๊ฑฐ๋ ์ด์ ๊ฒฝ๊ณ์์ ๋ฐ์์ด ํ๋ค๋ฆฌ๋ ํ์์ด ์ฌ๋ผ์ง๊ณ , ๋ฌธ์ฅ ์ ์ฒด์ ํธํก๊ณผ ์ฐ์์ด ๋ฌผ ํ๋ฅด๋ฏ ์ด์ด์ง๋๋ค. | |
| 3. **๋งํ๊ธฐ ์๋ ํ์คํ (`๋ฐํ ๋ฐฐ์ 0.85` + ๊ฐ์ ๋ณ ๋น ๋ฅด๊ธฐ `1.08 / 1.0 / 0.93`)**: | |
| - ์ด ๋ชจ๋ธ์ ๊ฐ์ฅ ์์ฐ์ค๋ฌ์ด ํ์ค ์ ์(`base length_scale`)์ **`0.85`** (`scales = [0.667, 0.85, 0.8]`)์ ๋๋ค. | |
| - ์ฌ๊ธฐ์ KoELECTRA ๊ฐ์ ๋ถ๋ฅ๊ธฐ์ `๋น ๋ฅด๊ธฐ` ๋จธ๋ฆฌ ์์ธก๊ฐ(`๋๋ฆฌ๊ฒ: 1.08`, `๋ณดํต: 1.0`, `๋น ๋ฅด๊ฒ: 0.93`, ํ์ ๋ `โฅ 0.4`์ผ ๋ ์ ์ฉ)์ ๊ณฑํ์ฌ ์ฌํยทํ์ ์ฅ๋ฉด์์๋ ์ฐจ๋ถํ๊ฒ(`0.85 ร 1.08 = 0.918`), ๊ธด๋ฐยท๋ถ๋ ธ ์ฅ๋ฉด์์๋ ์๋๊ฐ ์๊ฒ(`0.85 ร 0.93 = 0.7905`) ์๊ธ์ ์กฐ์ ํฉ๋๋ค. | |
| 4. **์ข ๊ฒฐํยท์ฐ๊ฒฐํ ์ด๋ฏธ์ ๋ฌธ์ฅ๋ถํธ์์ ๋์ด ์ฝ๊ธฐ (`๊ฐ๋ผ์ฝ๊ธฐ`)**: | |
| - ๊ธด ๋ฌธ์ฅ์ ๊ธ์ ์๋ ์ด์ ๋จ์๋ก ๋ฌด์์ ์๋ฅด๋ฉด ์ต์์ด ํ ๋ง ๋๊ณ , ๋ฐ๋๋ก ๊ธด ๋ฌธ๋จ์ ํต์งธ๋ก ํ ๋ฒ์ ํฉ์ฑํ๋ฉด ๋ค์ชฝ ํธํก์ด ๋ฌด๋์ง๊ฑฐ๋ ์ธ์ฝ๋๊ฐ ๊ฒฝ๊ณ๋ฅผ ๋์ด ๋ฐ์์ ํ๋ฆด ์ ์์ต๋๋ค. | |
| - **๊ฐ๋ผ์ฝ๊ธฐ**๋ ์ค์ง **๋ฌธ์ฅ๋ถํธ(`. ! ? , โฆ ; โ`)** ๋ฐ **ํ๊ตญ์ด ์ฐ๊ฒฐํ ์ด๋ฏธ(`~๊ณ `, `~๋ฉฐ`, `~๋ฉด์`, `~๋๋ฐ`, `~์ง๋ง`, `~์`[์กฐ์ฌ `์์ยท๊ป์ยท๋ก์` ์ ์ธ], `~๋๊น`, `~๋ค๊ฐ`, `~๊ฑฐ๋`, `~๋ ์ง`, `~๋ ค๊ณ `, `~์๋ง์` ๋ฑ โ ์ ์๋ถ์ฌ `๊ทธ๋ฆฌ๊ณ ยท๊ทธ๋์ยทํ์ง๋ง` ๋ฑ์ ์ ์ธ)**์ **์ข ๊ฒฐํ ์ด๋ฏธ(`~๋๋ค`, `~์ด์`, `~์์`, `~ํด์`, `~๋๋ค`, `~์๋ค`, `~์๋ค`, `~๋ค`, `~์`, `~๊น`, `~๋ค` ๋ฑ)** ๋ค์์๋ง ํธํก ๋จ์๋ก ๋๋์ด ๊ฐ๊ฐ ํฉ์ฑํฉ๋๋ค. | |
| - ๊ฐ ์กฐ๊ฐ ์๋ค์ ๋ถ์ธ ๊ฒฝ๊ณ ๊ธฐํธ(`^ _`, `$`)์ ์๋ฆฌ๋ ๋ชจ๋ธ์ด ๋ฐํํ **`durations` ๊ธธ์ดํ๋ก ์ ํํ ์๋ผ๋ด๊ณ **, ์กฐ๊ฐ ์ฌ์ด์ **`0.01์ด(10ms)`**์ ๋ฏธ์ธํ ์จํ์ ๋์ด ์ด์ด ๋ถ์์ผ๋ก์จ ํ ํธํก์ฒ๋ผ ๋งค๋๋ฝ๊ณ ์ ๋ช ํ ๋ญ๋ ์ ์์ฑํฉ๋๋ค. | |
| --- | |
| ## ๐ ํ์ผ ๊ตฌ์ฑ | |
| | ํ์ผ | ๋ด์ฉ | | |
| |---|---| | |
| | `piper-plus.onnx` | **๋ญ๋ ๋ชจ๋ธ (Epoch 880 EMA, 85.6MB)** โ ์ด์ ๊ฒฝ๊ณ ์ ๊ฑฐ ยท ๋์๋ฆฌ ๊ณผ์ ์ผ๋ฐํ ์ต์ ยท ์์ ๊ณต๋ช ํ | | |
| | `piper-plus.onnx.json` | ๋ชจ๋ธ ์ค์ โ ํ๋ณธ์จ(`22,050Hz`), ๊ธ์ยท์๊ธ์ยทํ๊ทธ ๋ฒํธํ(`phoneme_id_map`), ํ์ ๋ฒํธํ(`speaker_id_map`), ๊ธฐ๋ณธ ์ถ๋ก ๋ฐฐ์(`length_scale: 0.85`) | | |
| | `resonance/jamo_features.json` | ์๋ชจ ์ฑ์งํ (ํ๋ฏผ์ ์ ํด๋ก๋ณธ ๊ธฐ๋ฐ ๋ฐ์ ๊ณต๋ช ์ฃผํ์ 80์นธ + ํ๊ทธ ์๋น 24์นธ = 104์นธ) | | |
| | `resonance/speaker_features.json` | ์ฑ์ฐ ๋ฐ์ฑ ์ฃผํ์ 31๊ฐ์ง (ํ๊ท ยทํ์คํธ์ฐจ ๋ฐ ํ์๋ณ z ์ ์) | | |
| | `emotion/emotion_resonance.json` | ๊ฐ์ ยท๋งํฌ ์ด๋ฆ โ `res` ์ ๊ฒฝ๊ณต๋ช ์ ๋ ฅ ๊ฐ (๊ณต๋ช ๋ ธ๋ธ 3์นธ + ๊ฐ์ ๊ผฌ๋ฆฌ 15์นธ) | | |
| | `emotion/emotion_int8.onnx` | ๋ฌธ๋งฅ ๊ธฐ๋ฐ ๋ฌธ์ฅ ๊ฐ์ ยท๋น ๋ฅด๊ธฐยท๋งํฌ ์๋ ๋ถ๋ฅ ๋ชจ๋ธ (KoELECTRA-small v3 ๊ธฐ๋ฐ, int8, 14.6MB) | | |
| | `emotion/vocab.txt` | ๊ฐ์ ๋ชจ๋ธ ํ ํฌ๋์ด์ ์ดํ (WordPiece) | | |
| | `emotion/emotion_labels.json` ยท `emotion_labels_full.json` | ๊ฐ์ ๋ชจ๋ธ ์ถ๋ ฅ ๋จธ๋ฆฌ ๋ผ๋ฒจ ์ด๋ฆ๊ณผ ์ฐจ๋ก | | |
| | `emotion/emotion_config.json` | ๊ฐ์ ๋ชจ๋ธ ์ค์ (`max_len` 192 ๋ฑ) | | |
| | `emotion/emotion_tokens.json` | ๊ฐ์ ์ด๋ฆยท๋งํฌ ๋ฑ๋ง โ ๋ฒํธ ๋งคํ ํ | | |
| --- | |
| ## ๐ ๋ญ๋ ๋ชจ๋ธ ์ฐ๋ ๋ฒ | |
| ### ์ ๋ ฅ ํ ์ ๊ท๊ฒฉ | |
| | ์ด๋ฆ | ๋ชจ์ | ๋ป | | |
| |---|---|---| | |
| | `input` | `[1, n]` int64 | NFD ์๋ชจ ๋ฒํธ์ด (`^`, `_`, ์๋ชจ๋ค, `$`) | | |
| | `input_lengths` | `[1]` int64 | `n` | | |
| | `scales` | `[3]` float32 | `[noise_scale, length_scale, noise_w]` โ **ํ์ค ์ ์์ `[0.667, 0.85, 0.8]`** (`length_scale = 0.85 ร ๋น ๋ฅด๊ธฐ๋ฐฐ์`) | | |
| | `sid` | `[1]` int64 | ํ์ ๋ฒํธ (`speaker_id_map`) โ ๋ชจ๋ธ ๋ด๋ถ ์ฑ์ฐ ๊ณต๋ช ์ฃผํ์ํ์ ํ ๋ฒํธ | | |
| | `res` | `[1, 34]` float32 | ์ ๊ฒฝ๊ณต๋ช ์ ๋ ฅ. `[0:3]` ๊ณต๋ช ๋ ธ๋ธ, `[19:34]` ๊ฐ์ ๊ผฌ๋ฆฌ, `[3:19]` ๋ฏธ์ฌ์ฉ(0). ๋ชจ๋ 0์ด๋ฉด ๋ฌด๊ฐ์ ๊ธฐ๋ณธ ๋ชฉ์๋ฆฌ | | |
| ์ถ๋ ฅ `output`์ 22,050Hz ํํ(`-1.0 ~ 1.0`), `durations`๋ ๊ฐ ์ ๋ ฅ ํ ํฐ์ ํ๋ ์ ๊ธธ์ด(`hop_length = 256` ํ๋ณธ ๋จ์, ํ๊ทธ ์นธ์ ์ ๋ก๋๋ ์ด์ ์ผ๋ก ์๋ ์ ๊ฑฐ๋จ)์ ๋๋ค. | |
| ### ํ์ด์ฌ ์ ์ฒด ์์ (๋ถ๊ท์น ๋ฐ์ยท์์ด ์์ฐจ + ๊ฐ๋ผ์ฝ๊ธฐ + 0.85 ๋ฐฐ์ & ๊ฐ์ ๋น ๋ฅด๊ธฐ + ์ ๊ฒฝ๊ณต๋ช ) | |
| ```python | |
| import json, re, unicodedata, wave | |
| import numpy as np, onnxruntime as ort | |
| cfg = json.load(open("piper-plus.onnx.json", encoding="utf-8")) | |
| emo = json.load(open("emotion/emotion_resonance.json", encoding="utf-8")) | |
| ids_of = cfg["phoneme_id_map"] | |
| sr = cfg["audio"]["sample_rate"] # 22050 | |
| hop = cfg.get("hop_length", 256) # 256 | |
| # 1. ๋ถ๊ท์น ๋ฐ์ ์ฌ์ (๋์๋ฆฌ ๋ฑ) ๋ฐ ์์ด/์ฝฉ๊ธ๋ฆฌ์ฌยท์๊ธ์(แซยทแ ยทแฝ) ์์ฐจ ์์ | |
| IRREGULAR_DICT = {"์ธ๊ถ": "์ธ๊ฟ", "์ ๊ถ": "์ ๊ฟ", "์๊ฐ๋ฝ": "์๊น๋ฝ", "๋ฌผ๊ฐ": "๋ฌผ๊น", "์ธ๊ธฐ": "์ธ๋ผ"} | |
| ENGLISH_DICT = {"coffee": "์นดํผ", "thank": "์ํฌ", "very": "แซแ ฆ๋ฆฌ", "music": "๋ฎคแ แ ตแจ", "SNS": "์์ค์์์ค", "TV": "ํฐแซใ ฃ"} | |
| ALPHA_NAMES = { | |
| "A": "์์ด", "B": "๋น", "C": "์", "D": "๋", "E": "์ด", "F": "์ํ", "G": "์ง", | |
| "H": "์์ด์น", "I": "์์ด", "J": "์ ์ด", "K": "์ผ์ด", "L": "์", "M": "์ ", "N": "์", | |
| "O": "์ค", "P": "ํผ", "Q": "ํ", "R": "์๋ฅด", "S": "์์ค", "T": "ํฐ", "U": "์ ", | |
| "V": "แซแ ด", "W": "๋๋ธ์ ", "X": "์์ค", "Y": "์์ด", "Z": "แ แ ต", | |
| } | |
| def normalize_text(text): | |
| for k, v in sorted(IRREGULAR_DICT.items(), key=lambda x: -len(x[0])): | |
| text = text.replace(k, v) | |
| def repl_en(m): | |
| w = m.group(0) | |
| if w in ENGLISH_DICT: | |
| return ENGLISH_DICT[w] | |
| if w.lower() in ENGLISH_DICT: | |
| return ENGLISH_DICT[w.lower()] | |
| return "".join(ALPHA_NAMES.get(c.upper(), c) for c in w) | |
| return re.sub(r"[A-Za-z]+", repl_en, text) | |
| # 2. ๊ฐ๋ผ์ฝ๊ธฐ: ๋ฌธ์ฅ๋ถํธ ๋ฐ ํ๊ตญ์ด ์ฐ๊ฒฐํยท์ข ๊ฒฐํ ์ด๋ฏธ ์๋ฆฌ์์๋ง ํธํก ๋ถํ | |
| CONN_EOMI = ("๋ฉด์", "๋๋ฐ", "์ง๋ง", "๋๊น", "๋ค๊ฐ", "๊ฑฐ๋", "๋ ์ง", "๋ ค๊ณ ", "์๋ง์", "๊ณ ", "๋ฉฐ", "์") | |
| FINAL_EOMI = ( | |
| "๋๋ค", "์ด์", "์์", "์์", "์์", "ํด์", "์ธ์", "๋ค์", "๊ตฐ์", "์ง์", | |
| "๋๋ค", "์๋ค", "์๋ค", "์๋ค", "๊ฒ ๋ค", "ํ๋ค", "ํ๋ค", "๋ค", "์", "๊น", "๋ค", | |
| ) | |
| CONJ_ADV = {"๊ทธ๋ฆฌ๊ณ ", "๊ทธ๋ฌ๊ณ ", "๊ทธ๋์", "๊ทธ๋ฌ๋๊น", "ํ์ง๋ง", "๊ทธ๋ ์ง๋ง", "๊ทธ๋ฌ๋ค๊ฐ", "๊ทธ๋ฌ๋ฉด์"} | |
| PUNCT_CHARS = set(".!?,โฆ;โโโใ๏ผ๏ผ") | |
| def should_split_eojeol(eojeol): | |
| if eojeol in CONJ_ADV: | |
| return False | |
| if any(ch in PUNCT_CHARS for ch in eojeol): | |
| return True | |
| hangul = re.sub(r"[^๊ฐ-ํฃ]+$", "", eojeol) | |
| if len(hangul) < 2: | |
| return False | |
| for e in CONN_EOMI: | |
| if hangul.endswith(e) and len(hangul) > len(e): | |
| if e == "์" and hangul[-2] in ("์", "๊ป", "๋ก"): # ์กฐ์ฌ(์์ยท๊ป์ยท๋ก์) ์ ์ธ | |
| continue | |
| return True | |
| for e in FINAL_EOMI: | |
| if hangul.endswith(e) and len(hangul) > len(e): | |
| return True | |
| return False | |
| def split_by_eomi(text): | |
| words = text.strip().split() | |
| if len(words) <= 1: | |
| return [text.strip()] if text.strip() else [] | |
| chunks, cur = [], [] | |
| for i, w in enumerate(words): | |
| cur.append(w) | |
| if i < len(words) - 1 and should_split_eojeol(w): | |
| chunks.append(" ".join(cur)) | |
| cur = [] | |
| if cur: | |
| chunks.append(" ".join(cur)) | |
| return chunks | |
| # 3. NFD ์๋ชจ ๋ฒํธ์ด ๋ฐ ์ ๊ฒฝ๊ณต๋ช (res) ์์ฑ | |
| def to_ids(text): | |
| ids = ids_of["^"] + ids_of["_"] | |
| for ch in unicodedata.normalize("NFD", text): | |
| if ch in ids_of: | |
| ids += ids_of[ch] + ids_of["_"] | |
| return ids + ids_of["$"] | |
| def res_for(emotion=None, strength=1.0): | |
| r = np.zeros((1, emo["res"]["๊ธธ์ด"]), dtype=np.float32) | |
| if emotion: | |
| key = str(emo["๊ฐ์ "][emotion]) # ๋ฌด๊ฐ์ (250)์ ํ์ ์์ โ 0 | |
| knob, tail = emo["res"]["๋ ธ๋ธ"], emo["res"]["๊ฐ์ ๊ผฌ๋ฆฌ"] | |
| r[0, knob["์๋ฆฌ"]:knob["์๋ฆฌ"] + knob["์นธ"]] = np.array(emo["๋ ธ๋ธํ"].get(key, [0.0] * knob["์นธ"])) * knob["๋ฐฐ์"] * strength | |
| r[0, tail["์๋ฆฌ"]:tail["์๋ฆฌ"] + tail["์นธ"]] = np.array(emo["๊ผฌ๋ฆฌํ"].get(key, [0.0] * tail["์นธ"])) * tail["๋ฐฐ์"] * strength | |
| return r | |
| # 4. ํ์ค ์ ์(0.85) + ๊ฐ์ ๋ณ ๋น ๋ฅด๊ธฐ(1.08 / 1.0 / 0.93, ํ์ ๋ >= 0.4) + ๊ฐ๋ผ์ฝ๊ธฐ(0.01์ด ์ด์) ํฉ์ฑ | |
| BASE_SPEED = 0.85 | |
| SPEED_MULT = {"๋๋ฆฌ๊ฒ": 1.08, "๋ณดํต": 1.0, "๋น ๋ฅด๊ฒ": 0.93} | |
| sess = ort.InferenceSession("piper-plus.onnx", providers=["CPUExecutionProvider"]) | |
| def speak(text, speaker="nangdok", emotion=None, strength=1.0, speed_label="๋ณดํต", speed_conf=1.0, gap_sec=0.01): | |
| text = normalize_text(text) | |
| chunks = split_by_eomi(text) | |
| length_scale = BASE_SPEED * (SPEED_MULT.get(speed_label, 1.0) if speed_conf >= 0.4 else 1.0) | |
| scales = np.array([0.667, length_scale, 0.8], dtype=np.float32) | |
| sid = np.array([cfg["speaker_id_map"][speaker]], dtype=np.int64) | |
| res = res_for(emotion, strength) | |
| gap = np.zeros(int(sr * gap_sec), dtype=np.float32) | |
| head_len = len(ids_of["^"] + ids_of["_"]) | |
| tail_len = len(ids_of["$"]) | |
| pieces = [] | |
| for idx, chunk in enumerate(chunks): | |
| chunk_str = chunk + (" " if idx < len(chunks) - 1 else "") | |
| x = np.array([to_ids(chunk_str)], dtype=np.int64) | |
| out, durs = sess.run(None, { | |
| "input": x, | |
| "input_lengths": np.array([x.shape[1]], dtype=np.int64), | |
| "scales": scales, | |
| "sid": sid, | |
| "res": res, | |
| }) | |
| wav = out.squeeze() | |
| d = durs.squeeze() | |
| # ์กฐ๊ฐ ๊ฒฝ๊ณ์ ๋ง๋ถ์ธ ๋จธ๋ฆฌ(^ _)ยท๊ผฌ๋ฆฌ($) ๊ตฌ๊ฐ์ durations๋ก ์ ๋ฐํ๊ฒ ์๋ผ๋ | |
| trim_head = 0 if idx == 0 else int(round(float(d[:head_len].sum()) * hop)) | |
| trim_tail = len(wav) if idx == len(chunks) - 1 else int(round(float(d[:-tail_len].sum()) * hop)) | |
| trim_head = max(0, min(trim_head, len(wav))) | |
| trim_tail = max(trim_head, min(trim_tail, len(wav))) | |
| if idx > 0 and len(gap) > 0: | |
| pieces.append(gap) | |
| pieces.append(wav[trim_head:trim_tail]) | |
| return np.concatenate(pieces) if pieces else np.zeros(0, dtype=np.float32) | |
| # ๋ฌด๊ฐ์ ๊ธฐ๋ณธ ๋ญ๋ (์ด๋ฏธยท๋ฌธ์ฅ๋ถํธ ๊ฐ๋ผ์ฝ๊ธฐ + 0.85 ์ ์) | |
| audio = speak("์๋ ํ์ธ์. ์ค๋์ ๋ ์จ๊ฐ ์ฐธ ์ข์๊ณ , coffee ํ ์ ๋ง์๋ฉฐ ์ฐ์ฑ ํ๊ธฐ์๋ ์์ฃผ ์ข๋ค์.") | |
| # ๊ฐ์ ์ ๊ฒฝ๊ณต๋ช + ๋น ๋ฅด๊ธฐ ๊ฒฐํฉ ๋ญ๋ | |
| angry = speak("๋น์ฅ ์ฌ๊ธฐ์ ๋๊ฐ!", speaker="seungwoo", emotion="๋ถ๋ ธ", speed_label="๋น ๋ฅด๊ฒ") | |
| with wave.open("out.wav", "wb") as w: | |
| w.setnchannels(1); w.setsampwidth(2); w.setframerate(sr) | |
| w.writeframes((np.clip(audio, -1, 1) * 32767).astype(np.int16).tobytes()) | |
| ``` | |
| ### ํ์ ์๋ด (`speaker_id_map`) | |
| `speaker_id_map`์ 26์นธ์ด ์์ต๋๋ค. ๋ญ๋ (ํด์ค)์ `nangdok`(1), ์ฃผ์ ์ฑ์ฐ ๋ชฉ์๋ฆฌ๋ `seungwoo`(0) ยท `joyjjang`(2) ยท `seungchul`(3) ยท `์๋ผ`(6) ยท `์ง์ฐ`(23) ๋ฑ์ ๋๋ค. ๊ฐ ํ์๊ฐ ์ด๋ค 31์ฐจ์ ๋ฐ์ฑ ๊ณต๋ช ์ฃผํ์๋ก ๋ค์ด๊ฐ๋์ง๋ `resonance/speaker_features.json`์ ์๋ก๋์ด ์์ต๋๋ค. | |
| ## ๋ฌธ๋งฅ ๊ธฐ๋ฐ ์ ์ฉ ๊ฐ์ ๋ชจ๋ธ ์ฐ๋ ๋ฒ (`emotion/emotion_int8.onnx`) | |
| ์ฌ์ฉ์๊ฐ ์๋์ผ๋ก ๊ฐ์ ํ๊ทธ๋ฅผ ์ ๋ ฅํ์ง ์์๋ ์์ฐ์ค๋ฌ์ด ๊ฐ์ ํํ์ด ์ ๋๋๋๋ก ์ง์ ํ์ตํ ๋ฌธ๋งฅ ๊ธฐ๋ฐ ๊ฐ์ ๋ถ๋ฅ ๋ชจ๋ธ์ ๋๋ค: | |
| - **ํ ํฌ๋์ด์ **: `emotion/vocab.txt`์ WordPiece (KoELECTRA-small v3 ๋์ผ). `[CLS] ๋ฌธ์ฅ [SEP]`, ์ต๋ 192 ํ ํฐ. ์๋ค ๋ฌธ์ฅ ๋งฅ๋ฝ์ ํจ๊ป ์ค ๋๋ `tokenizer(๋งฅ๋ฝ๊ธ, ๋ฌธ์ฅ, truncation="only_first", max_length=192)` ํ์(์: `"์: ๋นํ. ๋ค: ๋ถ๋ ธ."`)์ผ๋ก ๋ฃ์ต๋๋ค. | |
| - **์ ๋ ฅ**: `input_ids`, `attention_mask`, `token_type_ids` (`[b, t]` int64). | |
| - **์ถ๋ ฅ ๋จธ๋ฆฌ** (`emotion/emotion_labels_full.json` ์ฐธ์กฐ): | |
| - ๋ฌธ์ฅ ๋จธ๋ฆฌ `[b, C]`: `๊ฐ์ `(16) ยท `ํฌ๊ธฐ`(3) ยท `๋น ๋ฅด๊ธฐ`(3: `๋๋ฆฌ๊ฒ`, `๋ณดํต`, `๋น ๋ฅด๊ฒ`) ยท `์จ๊ฒฐ`(4) ยท `๋งํฌ`(46) | |
| - ํ ํฐ ๋จธ๋ฆฌ `[b, t, C]`: `๋ผ์ธํ๊ทธ`(27) ยท `๊ตฌ๊ฐ`(11) | |
| - **์๋ ๋งคํ ํ๋ฆ**: `๊ฐ์ ` ๋ฐ `๋งํฌ` ๋จธ๋ฆฌ๊ฐ ๋ฌธ๋งฅ์์ ์ถ๋ก ํ ๊ฐ์ ยท๋งํฌ ๋ผ๋ฒจ์ `emotion_resonance.json`์ ํตํด 34์ฐจ์ `res` ๊ณต๋ช ๋ฒกํฐ๋ก ์๋ ๋ณํํ๊ณ , `๋น ๋ฅด๊ธฐ` ๋จธ๋ฆฌ์ ํ์ ๋๊ฐ `0.4` ์ด์์ด๋ฉด `SPEED_MULT` ๋ฐฐ์(`๋๋ฆฌ๊ฒ: 1.08`, `๋ณดํต: 1.0`, `๋น ๋ฅด๊ฒ: 0.93`)๋ฅผ ๊ธฐ๋ณธ ๋ฐฐ์ `0.85`์ ๊ณฑํด `scales`์ `length_scale`๋ก ์๋ ์ฃผ์ ํฉ๋๋ค. | |
| ## โ ๏ธ ๋ผ์ด์ ์ค ๋ฐ ์ ์์ ํ์ ์๋ฌด (License, Attribution & Citation) | |
| - **์์ฉยทํ์ยท์ฌ๊ฐ๋ฐ ์ ์์ ์์ ์ด๋์ (`watchpen`) ๋ฐ ๊ณต์ ๊นํ๋ธ ์ฃผ์ ์ฒจ๋ถ ์๋ฌด**: | |
| - ๋ณธ ๋ชจ๋ธ(`piper-plus`), 3๋ ์ ๊ฒฝ๊ณต๋ช ์ฃผํ์ ํ(`jamo_features.json`, `speaker_features.json`, `emotion_resonance.json`), ์๋ชจ ์ํ ๋ฐ์ฑ ๊ตฌ์กฐ, ์ ๋ก๋๋ ์ด์ ๊ฐ์ ํ๊ทธ ์ฒด๊ณ, ๋๋ ํฌํจ๋ ์ฝ๋/๊ฐ์ค์น๋ฅผ **์์ฉํ๊ฑฐ๋ ์์ ยทํ์ยท์ฌ๊ฐ๋ฐํ์ฌ ๋ฐฐํฌ/๊ณต๊ฐํ ๊ฒฝ์ฐ, ๋ฐ๋์ ์์ ์์ ์ด๋์ (`watchpen`)๊ณผ ๊ณต์ ๊นํ๋ธ ์ฃผ์(`https://github.com/wacthpen/piper-plus`)๋ฅผ ํจ๊ป ์ฒจ๋ถ(๋ช ์)**ํด์ผ ํฉ๋๋ค. | |
| - **Mandatory Attribution Requirement (English)**: *If you adapt, modify, fine-tune, or redevelop this model (`piper-plus`), its neural resonance frequency tables, its Jamo acoustic vocalization architecture, or its codebase, you **MUST** explicitly credit the original author's handle/initials (**`watchpen`** / GitHub: `wacthpen`, Hugging Face: `watchpen`) **AND** attach the official GitHub repository URL (**`https://github.com/wacthpen/piper-plus`**) in your documentation, model card, and derivative works.* | |
| - ์ค์ ์ฌ๋ ๋ชฉ์๋ฆฌ๋ฅผ ์ฌ์นญํ๊ฑฐ๋ ์์ด๋ ์ฉ๋๋ก ์ฌ์ฉํ์ง ๋ง์ธ์. | |
| - ๊ฐ์ ๋ถ๋ฅ ๋ชจ๋ธ์ ๊ธฐ๋ฐ ๋ชจ๋ธ์ [monologg/koelectra-small-v3-discriminator](https://huggingface.co/monologg/koelectra-small-v3-discriminator)์ ๋๋ค. | |
| ### ์ธ์ฉ (Citation / BibTeX) | |
| ```bibtex | |
| @misc{watchpen2026piperplus, | |
| author = {watchpen}, | |
| title = {Piper Plus: Korean Neural-Resonance TTS with Fortis-Overgeneralization Suppression, Emotion-Expressive Neural Resonance & Acoustic Jamo Vocalization}, | |
| year = {2026}, | |
| publisher = {GitHub / Hugging Face}, | |
| url = {https://github.com/wacthpen/piper-plus}, | |
| howpublished = {\url{https://github.com/wacthpen/piper-plus}, \url{https://huggingface.co/watchpen/piper-plus}} | |
| } | |
| ``` | |
| --- | |
| ## English Summary | |
| **Piper Plus (Epoch 880 EMA)** is a Korean multi-speaker neural text-to-speech model in the Piper (VITS) family exported to ONNX (22,050 Hz), engineered around four core innovations: | |
| 1. **Suppression of Excessive Fortis / Tensification Generalization**: | |
| - Traditional Korean TTS models allow the text encoder's embedding/context memory layer (`self.emb`) to over-generalize tensification from training words (e.g., learning fortis `[kk]` from `์ธ๊ถ [inkkwon]` and falsely tensifying lax consonants across ordinary words like `์๊ถ`). | |
| - **Piper Plus structurally bypasses the fortis-contaminated embedding memory layer (`jamo_feature_mode = "๋์ฒด"`, `x = torch.where(jamo_mask, emb_f(jamo_feat[x]), x)`)**, driving synthesis directly from 104-dimensional measured acoustic resonance vectors, paired with a **Korean Tenseness Discriminator (`tense_clf=True`, `tense_plain_weight=24.0` penalty for false tensification)** and **chest-resonance weight suppression (`res_band_weights='0,2,4'`)**. Lax consonants remain pure, while genuine irregular pronunciations are handled deterministically via a **1:1 Irregular Pronunciation Dictionary**. | |
| 2. **Emotion-Expressive Neural Resonance, Context-Aware Auto Emotion Model & Zero-Duration Tags**: | |
| - Emotions are injected via a 34-dimensional **Neural Resonance vector (`res`)** combining 3 vocal-tract resonance knobs (vocal-tract scale, chest resonance, actor's formant) and 15 measured voice-quality shifts (eGeMAPS 14 parameters + nasal coupling `A1-P0`). | |
| - A custom-trained **Context-Aware Korean Emotion Classifier (`emotion/emotion_int8.onnx`, KoELECTRA-small int8)** automatically analyzes sentence context to map emotion tags, speaking rate (`base length_scale = 0.85` ร `1.08 / 1.0 / 0.93`), and resonance parameters without manual tagging. | |
| - Any inline emotion/phonation tags impart their acoustic resonance onto surrounding Jamo inside the encoder and are then stripped with **Zero-Duration (`_ํ๊ทธ์นธ๋ผ๊ธฐ` scatter compaction, 0 frames)** before the Duration Predictor โ **producing pure emotional expression with zero spoken tag artifacts**. | |
| 3. **Acoustic Character Vocalization via Direct Jamo Learning & *Hunminjeongeum Haeryebon* Mathematical Realization**: | |
| - **49 Symbolic Columns Zeroed Out โ Only 31 Measured Acoustic Frequencies Active**: In `resonance/jamo_features.json` (80 base columns + 24 tag columns = 104 columns), all 49 symbolic/one-hot indicator columns (`letter ID`, `place`, `manner`, `tenseness`) are strictly set to `0.0` (`assert all(v == 0 ...)`), and `models.py` replaces `self.emb(x)` 100% with `self.emb_f(jamo_feat[x])`. The neural network never sees symbolic letter identities โ it synthesizes speech solely from **31 physical acoustic frequency measurements**: | |
| - **Initial Consonants (16 cols โ `ๅ่ฒๆ็ผๅไน็พฉ` & Five Sounds / Stroke Addition)**: Burst frequency/peak/spread, low-mid/mid-high energy ratios, aspiration duration/spectrum, intensity, F2/F3 locus shifts, voicing F1, and post-consonantal pitch. Initial `ใ ` is explicitly populated with measured glottal initiation frequencies per Haeryebon's `ใใ ใๅ้ณใๅฆๆฌฒๅญๅ็ผ่ฒใ` so consecutive vowels (`์์`, `์ผ์`) never fall back to `self.emb` or collapse. | |
| - **Medial Vowels (7 cols โ Heaven-Earth-Human `ใ, ใ ก, ใ ฃ` & Diphthong Trajectories)**: Formant frequencies at 10% (`F1ยทF2ยทF3 start`) and 75% (`F1ยทF2ยทF3 end`) plus vowel duration capture both monophthongs and compound diphthong trajectories. | |
| - **Final Codas (8 cols + 16 Liaison cols โ `็ต่ฒๅพฉ็จๅ่ฒ`, `ๅ ซๅญๅฏ่ถณ็จ`, `ไธๆทธไธๆฟ`, `่ชๅทฆ่ๅณ` in `ํด๋ก๋ณธ๋ฐ์นจ.py`)**: At Epoch 632, naive median ratios caused `๊ฐยท๊ฐยท๊ฐ` to collapse into `[์นธ]`. Implementing Haeryebon's five coda laws โ pairing homorganic stops & nasals (`ใฑยทใ` 1,503 Hz, `ใดยทใท` 1,875 Hz, `ใน` 1,813 Hz, `ใ ยทใ ` 559 Hz) for F2/F3 locus targets (`z2์ง, z3์ง`), mapping the 8-coda neutralization (`ๅ ซๅญๅฏ่ถณ็จ`), assigning voicing & nasal anti-resonance zeros (`๋ฐ์นจ์ฝง์๋ฆฌ๊ณจkHz`: `ใด 1.38 / ใ 1.30 / ใ 1.73 kHz`) exclusively to `ไธๆทธไธๆฟ` sonorants (`ใดยทในยทใ ยทใ `), and encoding double codas (`ใณ~ใ `) left-to-right (`่ชๅทฆ่ๅณ`: 1st consonant closure locus โ 2nd consonant locus + prevocalic liaison in the 16 initial columns) โ cleanly separated coda distances (`ใดโใ ` `0.98 โ 1.95`, `ใฑโใ ` `1.45 โ 3.72`). | |
| - **Archaic Hangul Jamo for Foreign/English Sounds & Konglish OOV Handling**: Archaic Hangul consonants (`แ ` `[f]`, `แซ`/`ใ ธ` `[v]`, `แ `/`ใ ฟ` `[z]`, `แผ` `[ฮธ]`, `แฝ` `[รฐ]`, IDs `384~388`) are populated with measured English fricative frequencies in the exact same 16 initial-consonant columns and vocalized through the single shared `emb_f` projection, while unseen English words and acronyms are seamlessly converted into natural Korean-style pronunciation (Konglish / ARPAbet-to-Hangul + alphabet vocalization). | |
| 4. **Ultra-Light, Near-Zero Heat on Mobile/CPU & Prosodic Ending Splitting (`๊ฐ๋ผ์ฝ๊ธฐ`)**: | |
| - Combining single linear resonance projections (`emb_f`, `emb_g_res`, `emb_res`) with a **4-subband MB-iSTFT-VITS (PQMF) decoder** makes inference extremely lightweight, running in real time on mobile phones and standard CPUs with **near-zero thermal output**. | |
| - Removes mechanical word-boundary (`eojeol_boundary`) CumSum breaks and splits long sentences only at punctuation and Korean connective/sentence-final verb endings (`split_by_eomi`), trimming boundary tokens precisely via `durations` and joining chunks with a `10 ms (0.01 s)` breath gap. | |
| --- | |
| ## ๐ ํ์ ๋ฐ ๊ธฐ๋ (Dedication) | |
| > **"์ฌํด๋ ํ๋ฏผ์ ์ ๋ฐํฌ 580๋์ ๋๋ค. ์ธ์ข ๋์์ ์๋ํฉ๋๋ค. ใํ๋ฏผ์ ์ ํด๋ก๋ณธใ์ ๋ณธ๋ ๋ฐ์ฑ ์ฃผํ์๋ฅผ ๋ง์ถ์ด ์ด ํ๋ก์ ํธ๋ฅผ ์์ฑํ์ต๋๋ค."** | |
| > | |
| > ๋ฐ์ฑ ๊ธฐ๊ด์ ๋ชจ์๊ณผ ์๋ฆฌ ์ด์น๋ฅผ ๋ณธ๋ฌ ใํ๋ฏผ์ ์ ํด๋ก๋ณธ(่จๆฐๆญฃ้ณ ่งฃไพๆฌ)ใ์ ์๋ํ ์ ์ ์๋ฆฌ๋ฅผ ํ๋ ์ ๊ฒฝ๋ง ์ํ ๋ฐ์ฑ ์ฃผํ์๋ก ๊ณ ์ค๋ํ ์ฎ๊ฒจ ๋ง์ถ๋ฉฐ, ํ๋ฏผ์ ์ ๋ฐํฌ 580๋(1446~2026)์ ๋ง์ ์ธ์ข ๋์์ ์ ๋ฏผ ์ ์ ๊ณผ ๊ณผํ์ ์ฑ์(่ฒ้ณ) ์ฒ ํ์ ๊น์ ๊ฒฝ์๋ฅผ ํํฉ๋๋ค. | |
| > | |
| > *This year marks the 580th anniversary of the promulgation of Hunminjeongeum (1446โ2026). King Sejong the Great is truly great. We conducted this project by aligning vocal resonance frequencies modeled after the Hunminjeongeum Haeryebon (่จๆฐๆญฃ้ณ ่งฃไพๆฌ).* | |