feat(tts): new default tone (1.35/-0.07/-0.30) + collapsible dashboard panel
- Change default voice controls to the user's tuned values: glyph speed 1.35, word_gap -0.07s (-70ms), sentence_gap -0.30s, pitch 0 (env defaults, slider initial values, and the 기본값 reset button all updated). - Make the "봇 목소리(TTS) 조절" dashboard panel collapsible via its header; starts collapsed (▸), expands on click (▾). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -12,9 +12,9 @@ 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 glyph speed / length_scale multiplier (0.5..2.0, default 1.25)
|
||||
WSAI_TTS_WORD_GAP intra-sentence pause delta, sec (-0.2..0.5, default 0.25)
|
||||
WSAI_TTS_SENTENCE_GAP sentence-boundary silence, sec (-0.5..1.5, default 0.75)
|
||||
WSAI_TTS_SPEED glyph speed / length_scale multiplier (0.5..2.0, default 1.35)
|
||||
WSAI_TTS_WORD_GAP intra-sentence pause delta, sec (-0.2..0.5, default -0.07)
|
||||
WSAI_TTS_SENTENCE_GAP sentence-boundary silence, sec (-0.5..1.5, default -0.30)
|
||||
WSAI_TTS_PITCH global semitone offset (-12..12, default 0.0)
|
||||
"""
|
||||
|
||||
@@ -99,12 +99,12 @@ 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.25"))
|
||||
else os.environ.get("WSAI_TTS_SPEED", "1.35"))
|
||||
# Reply-global rhythm/pitch controls (see melo_worker.py / docs manual).
|
||||
self.word_gap = float(word_gap if word_gap is not None
|
||||
else os.environ.get("WSAI_TTS_WORD_GAP", "0.25"))
|
||||
else os.environ.get("WSAI_TTS_WORD_GAP", "-0.07"))
|
||||
self.sentence_gap = float(sentence_gap if sentence_gap is not None
|
||||
else os.environ.get("WSAI_TTS_SENTENCE_GAP", "0.75"))
|
||||
else os.environ.get("WSAI_TTS_SENTENCE_GAP", "-0.30"))
|
||||
self.pitch = float(pitch if pitch is not None
|
||||
else os.environ.get("WSAI_TTS_PITCH", "0.0"))
|
||||
self.sink = sink or _log_sink
|
||||
|
||||
@@ -463,9 +463,9 @@ class Dashboard:
|
||||
def _tts_values(self) -> dict:
|
||||
t = self.tts
|
||||
return {
|
||||
"speed": float(getattr(t, "speed", 1.25)),
|
||||
"word_gap": float(getattr(t, "word_gap", 0.25)),
|
||||
"sentence_gap": float(getattr(t, "sentence_gap", 0.75)),
|
||||
"speed": float(getattr(t, "speed", 1.35)),
|
||||
"word_gap": float(getattr(t, "word_gap", -0.07)),
|
||||
"sentence_gap": float(getattr(t, "sentence_gap", -0.30)),
|
||||
"pitch": float(getattr(t, "pitch", 0.0)),
|
||||
}
|
||||
|
||||
@@ -723,6 +723,8 @@ PAGE = r"""<!DOCTYPE html>
|
||||
.demobar b{color:#ffe08a}
|
||||
.sttbox{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:14px 16px;margin:0 0 16px}
|
||||
.sttbox h2{font-size:13px;margin:0 0 10px;font-weight:600;color:var(--fg)}
|
||||
.sttbox h2.collap{cursor:pointer;user-select:none;margin:0}
|
||||
.sttbox h2.collap #ttsCaret{display:inline-block;width:12px;color:var(--muted)}
|
||||
.sttrow{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
|
||||
.btn{background:#173042;border:1px solid #234a63;color:var(--fg);border-radius:10px;padding:8px 14px;font-size:13px;cursor:pointer}
|
||||
.btn:hover{background:#1d3d54}
|
||||
@@ -865,25 +867,27 @@ PAGE = r"""<!DOCTYPE html>
|
||||
<div id="sttres" class="sttres"></div>
|
||||
</section>
|
||||
<section class="sttbox" id="ttsbox" style="display:none">
|
||||
<h2>🔊 봇 목소리(TTS) 조절 · MeloTTS</h2>
|
||||
<div class="ttsgrid">
|
||||
<label>글자 속도 <b id="tvSpeed">1.25x</b>
|
||||
<input type="range" id="tSpeed" min="0.5" max="2.0" step="0.05" value="1.25"></label>
|
||||
<label>단어 간격 <b id="tvWord">250 ms</b>
|
||||
<input type="range" id="tWord" min="-0.2" max="0.5" step="0.01" value="0.25"></label>
|
||||
<label>문장 간격 <b id="tvSent">0.75 s</b>
|
||||
<input type="range" id="tSent" min="-0.5" max="1.5" step="0.05" value="0.75"></label>
|
||||
<label>피치 <b id="tvPitch">0 반음</b>
|
||||
<input type="range" id="tPitch" min="-12" max="12" step="1" value="0"></label>
|
||||
<h2 id="ttsToggle" class="collap"><span id="ttsCaret">▸</span> 🔊 봇 목소리(TTS) 조절 · MeloTTS</h2>
|
||||
<div id="ttsBody" style="display:none">
|
||||
<div class="ttsgrid">
|
||||
<label>글자 속도 <b id="tvSpeed">1.35x</b>
|
||||
<input type="range" id="tSpeed" min="0.5" max="2.0" step="0.05" value="1.35"></label>
|
||||
<label>단어 간격 <b id="tvWord">-70 ms</b>
|
||||
<input type="range" id="tWord" min="-0.2" max="0.5" step="0.01" value="-0.07"></label>
|
||||
<label>문장 간격 <b id="tvSent">-0.30 s</b>
|
||||
<input type="range" id="tSent" min="-0.5" max="1.5" step="0.05" value="-0.3"></label>
|
||||
<label>피치 <b id="tvPitch">0 반음</b>
|
||||
<input type="range" id="tPitch" min="-12" max="12" step="1" value="0"></label>
|
||||
</div>
|
||||
<div class="sttrow">
|
||||
<input id="tText" class="ttstext" placeholder="미리들을 문장" value="안녕하세요, 목소리 미리듣기입니다.">
|
||||
<button id="tPreview" class="btn">▶ 미리듣기</button>
|
||||
<button id="tApply" class="btn">💾 봇에 적용</button>
|
||||
<button id="tReset" class="btn hbtn">기본값</button>
|
||||
<span id="tStat" class="sttstat">슬라이더로 조절 → 미리듣기로 확인 → 봇에 적용하면 다음 답변부터 반영됩니다.</span>
|
||||
</div>
|
||||
<audio id="tAudio" controls style="display:none;margin-top:10px;width:100%"></audio>
|
||||
</div>
|
||||
<div class="sttrow">
|
||||
<input id="tText" class="ttstext" placeholder="미리들을 문장" value="안녕하세요, 목소리 미리듣기입니다.">
|
||||
<button id="tPreview" class="btn">▶ 미리듣기</button>
|
||||
<button id="tApply" class="btn">💾 봇에 적용</button>
|
||||
<button id="tReset" class="btn hbtn">기본값</button>
|
||||
<span id="tStat" class="sttstat">슬라이더로 조절 → 미리듣기로 확인 → 봇에 적용하면 다음 답변부터 반영됩니다.</span>
|
||||
</div>
|
||||
<audio id="tAudio" controls style="display:none;margin-top:10px;width:100%"></audio>
|
||||
</section>
|
||||
<div class="demobar" id="demobar" style="display:none"></div>
|
||||
<div class="comp" id="comp"></div>
|
||||
@@ -1188,6 +1192,11 @@ function ttsSet(s){
|
||||
}
|
||||
async function initTts(){
|
||||
if(ttsInited) return; ttsInited = true;
|
||||
$('ttsToggle').onclick = ()=>{
|
||||
const b=$('ttsBody'), open=b.style.display==='none';
|
||||
b.style.display = open ? 'block' : 'none';
|
||||
$('ttsCaret').textContent = open ? '▾' : '▸';
|
||||
};
|
||||
['tSpeed','tWord','tSent','tPitch'].forEach(id => $(id).addEventListener('input', ttsLabels));
|
||||
$('tPreview').onclick = async ()=>{
|
||||
const text=($('tText').value||'').trim();
|
||||
@@ -1211,7 +1220,7 @@ async function initTts(){
|
||||
else { $('tStat').textContent='적용 실패: '+(j.error||''); }
|
||||
}catch(e){ $('tStat').textContent='적용 오류: '+e; }
|
||||
};
|
||||
$('tReset').onclick = ()=>{ ttsSet({speed:1.25,word_gap:0.25,sentence_gap:0.75,pitch:0});
|
||||
$('tReset').onclick = ()=>{ ttsSet({speed:1.35,word_gap:-0.07,sentence_gap:-0.3,pitch:0});
|
||||
$('tStat').textContent='기본값으로 되돌림 (미리듣기/적용을 눌러 반영).'; };
|
||||
try{
|
||||
const j=await (await fetch('/api/tts/settings')).json();
|
||||
|
||||
Reference in New Issue
Block a user