主动善后(用户反馈'善后不理想'):准确复核认出同人即撤销,不再被动等句子结束(长句错标挂几十秒):

- SpeakerRegistry.removeAuto(占位号清理)
- accurateRecheckAfterSwitch 的 CANCEL-LATE 分支→主线程 revertOnsetSwitch
- revert 两形态:占位行未建立(多数)→抹迹(预置作废/重开旧底行/防在途定格补封,零断行零闪烁);已建立→就地改签+并回+换色
- 仅当在途/落地切换仍指向该占位号时撤销,连环切换交被动链
This commit is contained in:
liwenhao2 2026-09-16 16:08:33 +08:00
parent d004345ec3
commit 898c5969e3
2 changed files with 47 additions and 2 deletions

View File

@ -1822,14 +1822,53 @@ class MainActivity : AppCompatActivity() {
val pre = SpeakerRecognition.registry.search(acc, speakerThreshold) val pre = SpeakerRecognition.registry.search(acc, speakerThreshold)
?: nearestAttribution(acc, SpeakerRecognition.registry, verdictFallbackMin) ?: nearestAttribution(acc, SpeakerRecognition.registry, verdictFallbackMin)
when { when {
pre != null && pre == active -> pre != null && pre == active -> {
Log.i(TAG, tag + "-CANCEL-LATE known=$pre(active) revert-via-verdict") Log.i(TAG, tag + "-CANCEL-LATE known=$pre(active) active-heal")
// 主动善后:不再被动等句子结束(长句让错标挂几十秒),立即撤销
uiHandler.post { revertOnsetSwitch(label, pre) }
}
pre != null -> pre != null ->
Log.i(TAG, tag + "-RECHECK known=$pre label=$label (verdict migrates)") Log.i(TAG, tag + "-RECHECK known=$pre label=$label (verdict migrates)")
else -> SpeakerRecognition.registry.addAuto(label, acc) else -> SpeakerRecognition.registry.addAuto(label, acc)
} }
} }
/** 乐观切换的主动善后(2026-09-16"善后不理想"整改):准确复核认出同一
* 人时立即撤销,不等句子结束——覆盖预置标签、还原钉住的标签、占位行
* 就地改签并与上方同人并回、清理快镜像占位编号。主线程执行。 */
private fun revertOnsetSwitch(firedLabel: String, prevActive: String?) {
if (prevActive == null) return // 无旧主可还原(会话极早期)→ 被动链
val bottom = subtitleLines.lastOrNull()
val pending = pendingOnsetLabel
val inFlight = pending == firedLabel ||
(bottom != null && bottom.label == firedLabel && bottom.open)
if (!inFlight) return // 状态已走远(连环切换等)→ 交给被动链
if (pending == firedLabel && bottom != null &&
bottom.label != firedLabel
) {
// 占位行尚未建立(补喂机制下多数如此):抹掉切换痕迹——预置标签
// 作废、重开旧底行,同人续说零断行,观感上从未切过
pendingOnsetLabel = null
onsetClosePending = false // 防在途定格回来补封行
if (!bottom.open) bottom.open = true
lastConfirmedLabel = prevActive
SpeakerRecognition.fastRegistry.removeAuto(firedLabel)
Log.i(TAG, "SWITCH-REVERT(pre-line) $firedLabel -> $prevActive")
return
}
// 占位行已建立:就地改签 + 与上方同人并回
pendingOnsetLabel = null
lastConfirmedLabel = prevActive
if (bottom != null && bottom.label == firedLabel) {
bottom.label = prevActive
bottom.colorIdx = colorIdxFor(prevActive)
val merged = mergeLineIntoPrevIfSame(bottom, prevActive)
if (!merged) notifyLine(bottom)
}
SpeakerRecognition.fastRegistry.removeAuto(firedLabel)
Log.i(TAG, "SWITCH-REVERT(line) $firedLabel -> $prevActive (active-heal)")
}
private fun appendNewLine(label: String?): SubtitleLine { private fun appendNewLine(label: String?): SubtitleLine {
// 新行出现即封掉之前的开放行,并把其中"未定格的进行中句子"整体迁移 // 新行出现即封掉之前的开放行,并把其中"未定格的进行中句子"整体迁移
// 过来 —— 硬保证 in-progress 只可能发生在最底行(修复:声纹判定搬家后, // 过来 —— 硬保证 in-progress 只可能发生在最底行(修复:声纹判定搬家后,

View File

@ -99,6 +99,12 @@ class SpeakerRegistry {
auto.add(Pair(name, l2normalize(e))) auto.add(Pair(name, l2normalize(e)))
} }
/** 撤销乐观切换时移除占位编号(2026-09-16:防被清掉的号继续被检索命中)。 */
@Synchronized
fun removeAuto(name: String) {
auto.removeAll { it.first == name }
}
// Enroll (or overwrite) a real name for a given embedding. // Enroll (or overwrite) a real name for a given embedding.
@Synchronized @Synchronized
fun addNamed(name: String, e: FloatArray) { fun addNamed(name: String, e: FloatArray) {