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)
|
unit, _ = self._trim_start_silence(unit, sr, budget)
|
||||||
pieces.append(unit)
|
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(
|
def synth_wav(
|
||||||
self,
|
self,
|
||||||
@@ -381,8 +422,8 @@ class MeloEngine(BaseEngine):
|
|||||||
"""세 가지를 독립적으로 조절:
|
"""세 가지를 독립적으로 조절:
|
||||||
- speed: 글자(음절) 발화 속도 — 모델 length_scale 로 각 음절의 발음
|
- speed: 글자(음절) 발화 속도 — 모델 length_scale 로 각 음절의 발음
|
||||||
속도를 생성 단계에서 조절(피치 보존, 사후 배속 아님)
|
속도를 생성 단계에서 조절(피치 보존, 사후 배속 아님)
|
||||||
- word_gap: 단어 사이 간격(초). >0 무음 삽입(어절 단위 합성),
|
- word_gap: 단어 사이 간격(초). 문장을 통째로 합성한 뒤 어절 사이 무음을
|
||||||
<0 경계 무음을 잘라 더 붙임, 0 자연스러운 문장 합성
|
>0 이면 늘리고 <0 이면 줄인다(억양 보존, 0 미만으로도 붙일 수 있음)
|
||||||
- sentence_gap: 문장 사이 간격(초). >0 무음 삽입, <0 경계 무음 트리밍
|
- sentence_gap: 문장 사이 간격(초). >0 무음 삽입, <0 경계 무음 트리밍
|
||||||
간격은 발화 속도와 무관하게 유지되어 서로 독립적이다.
|
간격은 발화 속도와 무관하게 유지되어 서로 독립적이다.
|
||||||
"""
|
"""
|
||||||
@@ -401,36 +442,23 @@ class MeloEngine(BaseEngine):
|
|||||||
if language in ("AUTO", "KR"):
|
if language in ("AUTO", "KR"):
|
||||||
text = normalize_korean(text)
|
text = normalize_korean(text)
|
||||||
|
|
||||||
# word_gap 이 0 이 아니면(양/음 모두) 어절 단위로 나눠 간격을 조절
|
|
||||||
use_word_mode = abs(word_gap) > 1e-4
|
|
||||||
sentences = split_sentences(text) or [text]
|
sentences = split_sentences(text) or [text]
|
||||||
sr: int | None = None
|
sr: int | None = None
|
||||||
pieces: list[np.ndarray] = []
|
pieces: list[np.ndarray] = []
|
||||||
first = True
|
first = True
|
||||||
|
|
||||||
for si, sent in enumerate(sentences):
|
for si, sent in enumerate(sentences):
|
||||||
sent_gap = 0.0 if first else sentence_gap
|
# 문장을 통째로 자연 합성(억양 유지) 후 어절 사이 무음만 조절
|
||||||
if use_word_mode:
|
a, s = self._synth_natural(sent, language, speaker, speed)
|
||||||
words = sent.split() or [sent]
|
if sr is None:
|
||||||
for wi, word in enumerate(words):
|
sr = s
|
||||||
a, s = self._synth_natural(word, language, speaker, speed)
|
if abs(word_gap) > 1e-4:
|
||||||
if sr is None:
|
a = self._scale_word_gaps(a, sr, word_gap)
|
||||||
sr = s
|
if first:
|
||||||
gap = sent_gap if wi == 0 else word_gap
|
pieces.append(a)
|
||||||
if first:
|
first = False
|
||||||
pieces.append(a)
|
|
||||||
first = False
|
|
||||||
else:
|
|
||||||
self._append_unit(pieces, a, gap, sr)
|
|
||||||
else:
|
else:
|
||||||
a, s = self._synth_natural(sent, language, speaker, speed)
|
self._append_unit(pieces, a, sentence_gap, sr)
|
||||||
if sr is None:
|
|
||||||
sr = s
|
|
||||||
if first:
|
|
||||||
pieces.append(a)
|
|
||||||
first = False
|
|
||||||
else:
|
|
||||||
self._append_unit(pieces, a, sent_gap, sr)
|
|
||||||
|
|
||||||
audio = np.concatenate(pieces) if pieces else np.zeros(1, dtype=np.float32)
|
audio = np.concatenate(pieces) if pieces else np.zeros(1, dtype=np.float32)
|
||||||
if sr is None:
|
if sr is None:
|
||||||
|
|||||||
Reference in New Issue
Block a user