Spaces:
Running on Zero
Running on Zero
Stop earlier without clipping final speech
Browse files- README.md +5 -4
- app.py +8 -4
- production.py +16 -2
- tests/test_production.py +15 -1
README.md
CHANGED
|
@@ -36,8 +36,8 @@ Demo 預設採用目前通過長文穩定性評估的推論設定:
|
|
| 36 |
| CFG | 2.0 |
|
| 37 |
| NFE steps | 10 |
|
| 38 |
| Target pace | 4.0 speech units/sec |
|
| 39 |
-
| Stop policy | 0.65 → 0.
|
| 40 |
-
| Hard stop | predicted target steps |
|
| 41 |
| Pace correction | pitch-preserving stretch after natural completion; first chunk maximum 24 chars |
|
| 42 |
| Maximum chunk | 80 chars |
|
| 43 |
| Minimum chunk | 12 chars |
|
|
@@ -50,8 +50,9 @@ Demo 預設採用目前通過長文穩定性評估的推論設定:
|
|
| 50 |
並依逗號、分號或句末標點插入不同長度的停頓。輸出最後會套用保守的 RMS floor 與 peak limit。
|
| 51 |
|
| 52 |
自然 stop 在目前 checkpoint 上仍可能過快或錯過句尾。Demo 不再用最低生成長度強迫模型繼續
|
| 53 |
-
發聲;模型自然完成文字後才做保音高語速校正,
|
| 54 |
-
|
|
|
|
| 55 |
|
| 56 |
請只使用已取得授權的參考音檔。合成語音僅供研究與評估展示,正式使用前請人工檢視。
|
| 57 |
|
|
|
|
| 36 |
| CFG | 2.0 |
|
| 37 |
| NFE steps | 10 |
|
| 38 |
| Target pace | 4.0 speech units/sec |
|
| 39 |
+
| Stop policy | 0.65 → 0.20 from 65% to 90% predicted progress, 2 consecutive hits |
|
| 40 |
+
| Hard stop | predicted target steps + 1 step |
|
| 41 |
| Pace correction | pitch-preserving stretch after natural completion; first chunk maximum 24 chars |
|
| 42 |
| Maximum chunk | 80 chars |
|
| 43 |
| Minimum chunk | 12 chars |
|
|
|
|
| 50 |
並依逗號、分號或句末標點插入不同長度的停頓。輸出最後會套用保守的 RMS floor 與 peak limit。
|
| 51 |
|
| 52 |
自然 stop 在目前 checkpoint 上仍可能過快或錯過句尾。Demo 不再用最低生成長度強迫模型繼續
|
| 53 |
+
發聲;模型自然完成文字後才做保音高語速校正,並在預估進度 65% 後逐步降低 stop threshold。
|
| 54 |
+
最後保留一個 latent step 的收尾空間,再以硬上界阻止多餘尾音。進階參數可供研究比較,但 release
|
| 55 |
+
profile 是 CFG 2.0、NFE 10、後處理語速 1.0。
|
| 56 |
|
| 57 |
請只使用已取得授權的參考音檔。合成語音僅供研究與評估展示,正式使用前請人工檢視。
|
| 58 |
|
app.py
CHANGED
|
@@ -58,10 +58,12 @@ CROSSFADE_MS = 80.0
|
|
| 58 |
CHUNK_EDGE_FADE_MS = 80.0
|
| 59 |
CHUNK_RMS_MATCH_DB = 4.0
|
| 60 |
STOP_THRESHOLD = 0.65
|
| 61 |
-
STOP_LATE_THRESHOLD = 0.
|
|
|
|
|
|
|
| 62 |
STOP_CONSECUTIVE = 2
|
| 63 |
HARD_STOP_RATIO = 1.0
|
| 64 |
-
HARD_STOP_MARGIN_STEPS =
|
| 65 |
MIN_PACE_SPEED = 0.80
|
| 66 |
MAX_TEXT_CHARS = 360
|
| 67 |
|
|
@@ -116,6 +118,8 @@ if not _NATIVE_STOP_POLICY:
|
|
| 116 |
threshold=STOP_THRESHOLD,
|
| 117 |
late_threshold=STOP_LATE_THRESHOLD,
|
| 118 |
consecutive=STOP_CONSECUTIVE,
|
|
|
|
|
|
|
| 119 |
)
|
| 120 |
model.stop_head = _STOP_CONTROLLER
|
| 121 |
|
|
@@ -187,7 +191,7 @@ def _generate_chunk(
|
|
| 187 |
"retry_badcase_ratio_threshold": 6.0,
|
| 188 |
}
|
| 189 |
if _NATIVE_STOP_POLICY:
|
| 190 |
-
kwargs["stop_threshold"] =
|
| 191 |
kwargs["stop_consecutive"] = STOP_CONSECUTIVE
|
| 192 |
if "generation_seed" in _GENERATE_PARAMETERS:
|
| 193 |
kwargs["generation_seed"] = request_seed
|
|
@@ -347,7 +351,7 @@ HEADER = f"""
|
|
| 347 |
台灣華語與中英混合文字轉語音。模型版本:`{CHECKPOINT}`。
|
| 348 |
|
| 349 |
目前預設採用穩定推論設定:`CFG 2.0`、`NFE 10`、目標語速 `4.0 字/秒`、
|
| 350 |
-
生成完成後校正至目標語速、
|
| 351 |
"""
|
| 352 |
|
| 353 |
|
|
|
|
| 58 |
CHUNK_EDGE_FADE_MS = 80.0
|
| 59 |
CHUNK_RMS_MATCH_DB = 4.0
|
| 60 |
STOP_THRESHOLD = 0.65
|
| 61 |
+
STOP_LATE_THRESHOLD = 0.20
|
| 62 |
+
STOP_LATE_START_RATIO = 0.65
|
| 63 |
+
STOP_LATE_FULL_RATIO = 0.90
|
| 64 |
STOP_CONSECUTIVE = 2
|
| 65 |
HARD_STOP_RATIO = 1.0
|
| 66 |
+
HARD_STOP_MARGIN_STEPS = 1
|
| 67 |
MIN_PACE_SPEED = 0.80
|
| 68 |
MAX_TEXT_CHARS = 360
|
| 69 |
|
|
|
|
| 118 |
threshold=STOP_THRESHOLD,
|
| 119 |
late_threshold=STOP_LATE_THRESHOLD,
|
| 120 |
consecutive=STOP_CONSECUTIVE,
|
| 121 |
+
late_start_ratio=STOP_LATE_START_RATIO,
|
| 122 |
+
late_full_ratio=STOP_LATE_FULL_RATIO,
|
| 123 |
)
|
| 124 |
model.stop_head = _STOP_CONTROLLER
|
| 125 |
|
|
|
|
| 191 |
"retry_badcase_ratio_threshold": 6.0,
|
| 192 |
}
|
| 193 |
if _NATIVE_STOP_POLICY:
|
| 194 |
+
kwargs["stop_threshold"] = STOP_THRESHOLD
|
| 195 |
kwargs["stop_consecutive"] = STOP_CONSECUTIVE
|
| 196 |
if "generation_seed" in _GENERATE_PARAMETERS:
|
| 197 |
kwargs["generation_seed"] = request_seed
|
|
|
|
| 351 |
台灣華語與中英混合文字轉語音。模型版本:`{CHECKPOINT}`。
|
| 352 |
|
| 353 |
目前預設採用穩定推論設定:`CFG 2.0`、`NFE 10`、目標語速 `4.0 字/秒`、
|
| 354 |
+
生成完成後校正至目標語速、提前降低 stop threshold、target + 1 step hard stop、每 80 字切段,且不使用 retry 或 rerank。
|
| 355 |
"""
|
| 356 |
|
| 357 |
|
production.py
CHANGED
|
@@ -42,12 +42,19 @@ class StopHysteresisController(nn.Module):
|
|
| 42 |
threshold: float = 0.65,
|
| 43 |
late_threshold: float = 0.50,
|
| 44 |
consecutive: int = 2,
|
|
|
|
|
|
|
| 45 |
) -> None:
|
| 46 |
super().__init__()
|
| 47 |
self.stop_head = stop_head
|
| 48 |
self.threshold = float(threshold)
|
| 49 |
self.late_threshold = float(late_threshold)
|
| 50 |
self.consecutive = max(1, int(consecutive))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
self._active = False
|
| 52 |
self._min_len = 2
|
| 53 |
self._expected_steps = 0
|
|
@@ -82,8 +89,15 @@ class StopHysteresisController(nn.Module):
|
|
| 82 |
threshold = self.threshold
|
| 83 |
if self._expected_steps > 0:
|
| 84 |
progress = generated_steps / float(self._expected_steps)
|
| 85 |
-
if progress >=
|
| 86 |
-
blend = min(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
threshold = self.threshold + blend * (self.late_threshold - self.threshold)
|
| 88 |
eligible = self._step > self._min_len
|
| 89 |
if eligible and probability >= threshold:
|
|
|
|
| 42 |
threshold: float = 0.65,
|
| 43 |
late_threshold: float = 0.50,
|
| 44 |
consecutive: int = 2,
|
| 45 |
+
late_start_ratio: float = 0.80,
|
| 46 |
+
late_full_ratio: float = 1.00,
|
| 47 |
) -> None:
|
| 48 |
super().__init__()
|
| 49 |
self.stop_head = stop_head
|
| 50 |
self.threshold = float(threshold)
|
| 51 |
self.late_threshold = float(late_threshold)
|
| 52 |
self.consecutive = max(1, int(consecutive))
|
| 53 |
+
self.late_start_ratio = max(0.0, float(late_start_ratio))
|
| 54 |
+
self.late_full_ratio = max(
|
| 55 |
+
self.late_start_ratio + 1.0e-6,
|
| 56 |
+
float(late_full_ratio),
|
| 57 |
+
)
|
| 58 |
self._active = False
|
| 59 |
self._min_len = 2
|
| 60 |
self._expected_steps = 0
|
|
|
|
| 89 |
threshold = self.threshold
|
| 90 |
if self._expected_steps > 0:
|
| 91 |
progress = generated_steps / float(self._expected_steps)
|
| 92 |
+
if progress >= self.late_start_ratio:
|
| 93 |
+
blend = min(
|
| 94 |
+
1.0,
|
| 95 |
+
max(
|
| 96 |
+
0.0,
|
| 97 |
+
(progress - self.late_start_ratio)
|
| 98 |
+
/ (self.late_full_ratio - self.late_start_ratio),
|
| 99 |
+
),
|
| 100 |
+
)
|
| 101 |
threshold = self.threshold + blend * (self.late_threshold - self.threshold)
|
| 102 |
eligible = self._step > self._min_len
|
| 103 |
if eligible and probability >= threshold:
|
tests/test_production.py
CHANGED
|
@@ -60,6 +60,20 @@ def test_stop_controller_relaxes_near_endpoint_and_enforces_hard_stop():
|
|
| 60 |
assert decisions == [0, 0, 0, 1]
|
| 61 |
|
| 62 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
def test_split_preserves_punctuation_and_minimum_chunk_size():
|
| 64 |
text = (
|
| 65 |
"第一句很短。第二句也不長。第三句需要再多一些文字,才能測試切段是否正確。"
|
|
@@ -82,7 +96,7 @@ def test_duration_units_and_target_pace_min_len():
|
|
| 82 |
stop_consecutive=2,
|
| 83 |
) == 5
|
| 84 |
assert target_cps_steps("一二三四五六七八", target_cps=4.0, step_seconds=0.25) == 8
|
| 85 |
-
assert duration_hard_stop_steps(55, ratio=1.0, margin_steps=
|
| 86 |
assert duration_hard_stop_steps(0, ratio=1.08, margin_steps=3) == 2000
|
| 87 |
|
| 88 |
|
|
|
|
| 60 |
assert decisions == [0, 0, 0, 1]
|
| 61 |
|
| 62 |
|
| 63 |
+
def test_stop_controller_can_relax_before_the_text_pace_cap():
|
| 64 |
+
controller = StopHysteresisController(
|
| 65 |
+
_SequenceStopHead([0.4] * 9),
|
| 66 |
+
threshold=0.65,
|
| 67 |
+
late_threshold=0.20,
|
| 68 |
+
consecutive=2,
|
| 69 |
+
late_start_ratio=0.65,
|
| 70 |
+
late_full_ratio=0.90,
|
| 71 |
+
)
|
| 72 |
+
controller.begin(min_len=2, expected_steps=10, hard_stop_steps=11)
|
| 73 |
+
decisions = [controller(torch.zeros(1, 2)).argmax(dim=-1).item() for _ in range(9)]
|
| 74 |
+
assert decisions == [0, 0, 0, 0, 0, 0, 0, 0, 1]
|
| 75 |
+
|
| 76 |
+
|
| 77 |
def test_split_preserves_punctuation_and_minimum_chunk_size():
|
| 78 |
text = (
|
| 79 |
"第一句很短。第二句也不長。第三句需要再多一些文字,才能測試切段是否正確。"
|
|
|
|
| 96 |
stop_consecutive=2,
|
| 97 |
) == 5
|
| 98 |
assert target_cps_steps("一二三四五六七八", target_cps=4.0, step_seconds=0.25) == 8
|
| 99 |
+
assert duration_hard_stop_steps(55, ratio=1.0, margin_steps=1) == 56
|
| 100 |
assert duration_hard_stop_steps(0, ratio=1.08, margin_steps=3) == 2000
|
| 101 |
|
| 102 |
|