feat(tts): add [기본] neutral emotion + raise base speed to 1.5x

User reported the emotion samples sounded the same and the speed change wasn't
noticeable. Two changes:
- Add a "base" (기본/neutral) emotion at speed 1.0x so the plain base voice can
  be selected explicitly via a [기본] tag and auditioned against the others.
- Bump the WSAI_TTS_SPEED default 1.15 -> 1.5 for a clearly faster base voice.
  Emotion multipliers scale off base, so every emotion speeds up together.

Also extends gen_emotion_samples.py to emit one wav per emotion (incl. 기본)
plus a stitched all-in-one, so each emotion can be delivered as a separate clip.

Verified: 29 tests pass; match_emotion('기본') == 'base'; per-emotion synthesis
at base 1.5 produces distinct clip durations (base 4.9s vs happy 3.3s).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
EJClaw
2026-08-23 00:08:21 +09:00
parent d410d4a6b5
commit e67cd2e93f
3 changed files with 47 additions and 38 deletions

View File

@@ -30,6 +30,7 @@ from dataclasses import dataclass
# artefact-free. The pitch column is retained (rather than removed) so the effect
# can be re-enabled per-emotion later with a real, artefact-free pitch method.
EMOTION_PARAMS: dict[str, tuple[float, float]] = {
"base": (1.00, 0.0), # 기본 / neutral — the plain base voice, no colour
"happy": (1.08, 0.0), # 기쁨 / cheerful
"excited": (1.15, 0.0), # 신남 / excited
"hopeful": (1.10, 0.0), # 희망 / 힘차게
@@ -69,6 +70,7 @@ def _norm(word: str) -> str:
return re.sub(r"\s+", "", word).lower()
_register("base", "기본", "기본목소리", "기본톤", "보통", "평범", "무감정", "default", "neutral", "normal", "plain")
_register("happy", "기쁨", "기쁘게", "기뻐", "기뻐하며", "행복", "행복하게", "행복하게도", "즐겁게", "즐거움", "밝게", "반가움", "반갑게", "반가워", "cheerful", "happy", "joyful")
_register("excited", "신남", "신나게", "신나서", "흥분", "들뜬", "들떠서", "설렘", "설레며", "excited", "thrilled")
_register("hopeful", "희망", "희망차게", "힘차게", "힘내", "힘내서", "응원", "응원하며", "격려", "hopeful", "encouraging")

View File

@@ -12,7 +12,7 @@ Env:
WSAI_MELO_DEVICE cpu | cuda | auto (default auto: GPU if torch sees one,
else CPU; the worker falls back to CPU if CUDA fails)
WSAI_TTS_OUT_DIR where wavs are written (default ~/.cache/wsai/tts)
WSAI_TTS_SPEED synthesis speed multiplier (default 1.15)
WSAI_TTS_SPEED synthesis speed multiplier (default 1.5)
"""
from __future__ import annotations
@@ -93,7 +93,7 @@ class MeloTTS:
self.out_dir = Path(out_dir or os.environ.get("WSAI_TTS_OUT_DIR")
or (Path.home() / ".cache/wsai/tts"))
self.speed = float(speed if speed is not None
else os.environ.get("WSAI_TTS_SPEED", "1.15"))
else os.environ.get("WSAI_TTS_SPEED", "1.5"))
self.sink = sink or _log_sink
self._proc: asyncio.subprocess.Process | None = None
self._lock = asyncio.Lock()