fix: 단어 간격을 무음 스케일링으로 전환(자연 0 기준 아래로도 축소)
- 어절 개별 합성(억양 저하, 0 미만 불가) 대신, 문장을 통째로 자연 합성한 뒤 내부 어절 무음 구간을 _scale_word_gaps 로 늘이거나 줄인다 - word_gap<0 이 자연 상태(0)보다 더 촘촘해짐(요청 충족), 억양 보존 - min_pause(80ms) 미만 짧은 무음/경계 무음은 건드리지 않아 발음 안전
This commit is contained in:
@@ -367,6 +367,47 @@ class MeloEngine(BaseEngine):
|
||||
unit, _ = self._trim_start_silence(unit, sr, budget)
|
||||
pieces.append(unit)
|
||||
|
||||
def _scale_word_gaps(
|
||||
self,
|
||||
a: np.ndarray,
|
||||
sr: int,
|
||||
delta: float,
|
||||
thresh: float = 0.02,
|
||||
min_pause: float = 0.08,
|
||||
) -> np.ndarray:
|
||||
"""자연 합성된 문장 오디오에서 '어절 사이 무음'을 delta 초만큼 조절.
|
||||
|
||||
- 억양 보존: 문장을 통째로 합성한 결과의 내부 무음 구간만 늘리거나 줄인다.
|
||||
- delta>0 이면 무음을 늘려 단어를 벌리고, delta<0 이면 무음을 줄여 붙인다.
|
||||
- 앞/뒤 경계 무음과 min_pause 미만의 짧은 무음(자음 폐쇄 등)은 건드리지 않아
|
||||
발음이 깨지지 않는다.
|
||||
"""
|
||||
n = a.size
|
||||
if abs(delta) < 1e-4 or n == 0:
|
||||
return a
|
||||
silent = np.abs(a) < thresh
|
||||
changes = np.flatnonzero(np.diff(silent.astype(np.int8)) != 0) + 1
|
||||
bounds = [0, *changes.tolist(), n]
|
||||
min_n = int(sr * min_pause)
|
||||
floor_n = int(sr * 0.015)
|
||||
add_n = int(delta * sr)
|
||||
out: list[np.ndarray] = []
|
||||
last = len(bounds) - 2
|
||||
for k in range(len(bounds) - 1):
|
||||
s, e = bounds[k], bounds[k + 1]
|
||||
seg = a[s:e]
|
||||
is_internal = 0 < k < last # 앞/뒤 경계 구간은 제외
|
||||
if silent[s] and is_internal and (e - s) >= min_n:
|
||||
new_n = max(floor_n, (e - s) + add_n)
|
||||
if new_n >= (e - s):
|
||||
seg = np.concatenate(
|
||||
[seg, np.zeros(new_n - (e - s), dtype=np.float32)]
|
||||
)
|
||||
else:
|
||||
seg = seg[:new_n]
|
||||
out.append(seg)
|
||||
return np.concatenate(out) if out else a
|
||||
|
||||
# ---- 합성 -----------------------------------------------------
|
||||
def synth_wav(
|
||||
self,
|
||||
@@ -381,8 +422,8 @@ class MeloEngine(BaseEngine):
|
||||
"""세 가지를 독립적으로 조절:
|
||||
- speed: 글자(음절) 발화 속도 — 모델 length_scale 로 각 음절의 발음
|
||||
속도를 생성 단계에서 조절(피치 보존, 사후 배속 아님)
|
||||
- word_gap: 단어 사이 간격(초). >0 무음 삽입(어절 단위 합성),
|
||||
<0 경계 무음을 잘라 더 붙임, 0 자연스러운 문장 합성
|
||||
- word_gap: 단어 사이 간격(초). 문장을 통째로 합성한 뒤 어절 사이 무음을
|
||||
>0 이면 늘리고 <0 이면 줄인다(억양 보존, 0 미만으로도 붙일 수 있음)
|
||||
- sentence_gap: 문장 사이 간격(초). >0 무음 삽입, <0 경계 무음 트리밍
|
||||
간격은 발화 속도와 무관하게 유지되어 서로 독립적이다.
|
||||
"""
|
||||
@@ -401,36 +442,23 @@ class MeloEngine(BaseEngine):
|
||||
if language in ("AUTO", "KR"):
|
||||
text = normalize_korean(text)
|
||||
|
||||
# word_gap 이 0 이 아니면(양/음 모두) 어절 단위로 나눠 간격을 조절
|
||||
use_word_mode = abs(word_gap) > 1e-4
|
||||
sentences = split_sentences(text) or [text]
|
||||
sr: int | None = None
|
||||
pieces: list[np.ndarray] = []
|
||||
first = True
|
||||
|
||||
for si, sent in enumerate(sentences):
|
||||
sent_gap = 0.0 if first else sentence_gap
|
||||
if use_word_mode:
|
||||
words = sent.split() or [sent]
|
||||
for wi, word in enumerate(words):
|
||||
a, s = self._synth_natural(word, language, speaker, speed)
|
||||
if sr is None:
|
||||
sr = s
|
||||
gap = sent_gap if wi == 0 else word_gap
|
||||
if first:
|
||||
pieces.append(a)
|
||||
first = False
|
||||
else:
|
||||
self._append_unit(pieces, a, gap, sr)
|
||||
# 문장을 통째로 자연 합성(억양 유지) 후 어절 사이 무음만 조절
|
||||
a, s = self._synth_natural(sent, language, speaker, speed)
|
||||
if sr is None:
|
||||
sr = s
|
||||
if abs(word_gap) > 1e-4:
|
||||
a = self._scale_word_gaps(a, sr, word_gap)
|
||||
if first:
|
||||
pieces.append(a)
|
||||
first = False
|
||||
else:
|
||||
a, s = self._synth_natural(sent, language, speaker, speed)
|
||||
if sr is None:
|
||||
sr = s
|
||||
if first:
|
||||
pieces.append(a)
|
||||
first = False
|
||||
else:
|
||||
self._append_unit(pieces, a, sent_gap, sr)
|
||||
self._append_unit(pieces, a, sentence_gap, sr)
|
||||
|
||||
audio = np.concatenate(pieces) if pieces else np.zeros(1, dtype=np.float32)
|
||||
if sr is None:
|
||||
|
||||
Reference in New Issue
Block a user