From 62119857303da38859c2112a8d6341429a5cd0cc Mon Sep 17 00:00:00 2001 From: Claude Owner Date: Tue, 26 May 2026 21:04:27 +0900 Subject: [PATCH] fix(melo-test): strip standalone Hangul jamo before synthesis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- melo-test/README.md | 5 +++++ melo-test/server.py | 29 ++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/melo-test/README.md b/melo-test/README.md index 10f249a..ad00285 100644 --- a/melo-test/README.md +++ b/melo-test/README.md @@ -114,6 +114,11 @@ Content-Type: application/json 한국어 발음이 깨짐 → 입력에 영문/이모지가 섞이면 운율이 어색해질 수 있음. 디스코드 봇 통합 단계에서 `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` → `unidic` 사전 데이터 미다운로드. 가상환경 활성화 상태에서 `python -m unidic download` 실행 후 재시작. 한국어만 써도 MeloTTS 가 임포트 시점에 일본어 모듈을 강제 로딩하기 때문에 이 단계는 필수. diff --git a/melo-test/server.py b/melo-test/server.py index c8c3529..449fa94 100644 --- a/melo-test/server.py +++ b/melo-test/server.py @@ -8,6 +8,7 @@ GET / -> index.html (테스트 UI) """ import os import io +import re import time import tempfile import logging @@ -62,16 +63,38 @@ class TTSReq(BaseModel): 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") def tts(req: TTSReq): model = state.get("model") if model is None: raise HTTPException(503, "model not ready") - text = (req.text or "").strip() - if not text: + raw = (req.text or "").strip() + if not raw: raise HTTPException(400, "empty text") - if len(text) > 500: + if len(raw) > 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))) fd, out_path = tempfile.mkstemp(suffix=".wav")