fix(melo-test): strip standalone Hangul jamo before synthesis
MeloTTS-Korean's symbol vocab only includes precomposed Hangul syllables. Bare Hangul Compatibility Jamo characters (ㅋㅋㅋ, ㅠㅠ, ㅎㅎ) and Hangul Jamo block code points blow up in `cleaned_text_to_sequence` with `KeyError: 'ㅋ'` (or similar). Pre-clean the request text in `/tts` to drop those code point ranges before handing the string to the model, collapsing the leftover whitespace. If sanitization yields an empty string, return 400 with an explanation instead of a 500 stack trace. Also document the limitation in the README troubleshooting section.
This commit is contained in:
@@ -114,6 +114,11 @@ Content-Type: application/json
|
|||||||
한국어 발음이 깨짐
|
한국어 발음이 깨짐
|
||||||
→ 입력에 영문/이모지가 섞이면 운율이 어색해질 수 있음. 디스코드 봇 통합 단계에서 `TTSClient.textReplace` 같은 사전 치환을 그대로 활용.
|
→ 입력에 영문/이모지가 섞이면 운율이 어색해질 수 있음. 디스코드 봇 통합 단계에서 `TTSClient.textReplace` 같은 사전 치환을 그대로 활용.
|
||||||
|
|
||||||
|
`KeyError: 'ㅋ'` (또는 'ㅠ', 'ㅎ' 등)
|
||||||
|
→ MeloTTS-Korean 의 symbol vocab 은 완성형 음절(가, 나, ...)만 포함하므로 'ㅋㅋㅋ', 'ㅠㅠ' 같은 한글 자모(Hangul Compatibility Jamo) 가 단독으로 들어오면 합성 단계에서 죽음.
|
||||||
|
`server.py` 가 요청 직전에 단독 자모를 자동으로 제거하므로 최신 코드(`git pull`) 받으면 해결.
|
||||||
|
봇 통합 단계에서도 어댑터에서 같은 sanitize 를 적용할 것.
|
||||||
|
|
||||||
`RuntimeError: Failed initializing MeCab` / `no such file or directory: ...\unidic\dicdir\mecabrc`
|
`RuntimeError: Failed initializing MeCab` / `no such file or directory: ...\unidic\dicdir\mecabrc`
|
||||||
→ `unidic` 사전 데이터 미다운로드. 가상환경 활성화 상태에서 `python -m unidic download` 실행 후 재시작.
|
→ `unidic` 사전 데이터 미다운로드. 가상환경 활성화 상태에서 `python -m unidic download` 실행 후 재시작.
|
||||||
한국어만 써도 MeloTTS 가 임포트 시점에 일본어 모듈을 강제 로딩하기 때문에 이 단계는 필수.
|
한국어만 써도 MeloTTS 가 임포트 시점에 일본어 모듈을 강제 로딩하기 때문에 이 단계는 필수.
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ GET / -> index.html (테스트 UI)
|
|||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import io
|
import io
|
||||||
|
import re
|
||||||
import time
|
import time
|
||||||
import tempfile
|
import tempfile
|
||||||
import logging
|
import logging
|
||||||
@@ -62,16 +63,38 @@ class TTSReq(BaseModel):
|
|||||||
speed: float = 1.0
|
speed: float = 1.0
|
||||||
|
|
||||||
|
|
||||||
|
# MeloTTS-Korean 의 symbol vocab 은 완성형 음절(가, 나, ...)만 포함한다.
|
||||||
|
# 'ㅋㅋㅋ', 'ㅠㅠ', 'ㅎㅎ' 같이 한글 자모(Hangul Compatibility Jamo /
|
||||||
|
# Hangul Jamo) 가 단독으로 들어오면 cleaned_text_to_sequence 에서
|
||||||
|
# KeyError 로 죽는다. 합성 전에 미리 제거한다.
|
||||||
|
_STANDALONE_JAMO_RE = re.compile(r"[\u1100-\u11FF\u3130-\u318F\uA960-\uA97F\uD7B0-\uD7FF]+")
|
||||||
|
|
||||||
|
|
||||||
|
def _sanitize_for_melo(text: str) -> str:
|
||||||
|
# 단독 자모를 공백으로 치환한 뒤 연속 공백을 하나로 정리.
|
||||||
|
cleaned = _STANDALONE_JAMO_RE.sub(" ", text)
|
||||||
|
return re.sub(r"\s+", " ", cleaned).strip()
|
||||||
|
|
||||||
|
|
||||||
@app.post("/tts")
|
@app.post("/tts")
|
||||||
def tts(req: TTSReq):
|
def tts(req: TTSReq):
|
||||||
model = state.get("model")
|
model = state.get("model")
|
||||||
if model is None:
|
if model is None:
|
||||||
raise HTTPException(503, "model not ready")
|
raise HTTPException(503, "model not ready")
|
||||||
text = (req.text or "").strip()
|
raw = (req.text or "").strip()
|
||||||
if not text:
|
if not raw:
|
||||||
raise HTTPException(400, "empty text")
|
raise HTTPException(400, "empty text")
|
||||||
if len(text) > 500:
|
if len(raw) > 500:
|
||||||
raise HTTPException(400, "too long (<=500)")
|
raise HTTPException(400, "too long (<=500)")
|
||||||
|
text = _sanitize_for_melo(raw)
|
||||||
|
if not text:
|
||||||
|
raise HTTPException(
|
||||||
|
400,
|
||||||
|
"text contains only standalone Hangul jamo (e.g. ㅋㅋㅋ); "
|
||||||
|
"MeloTTS-Korean cannot synthesize these.",
|
||||||
|
)
|
||||||
|
if text != raw:
|
||||||
|
log.info("sanitized standalone jamo: %r -> %r", raw[:60], text[:60])
|
||||||
|
|
||||||
speed = max(0.5, min(2.0, float(req.speed or 1.0)))
|
speed = max(0.5, min(2.0, float(req.speed or 1.0)))
|
||||||
fd, out_path = tempfile.mkstemp(suffix=".wav")
|
fd, out_path = tempfile.mkstemp(suffix=".wav")
|
||||||
|
|||||||
Reference in New Issue
Block a user