diff --git a/app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt b/app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt index 7c44163..90358bd 100644 --- a/app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt +++ b/app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt @@ -1822,14 +1822,53 @@ class MainActivity : AppCompatActivity() { val pre = SpeakerRecognition.registry.search(acc, speakerThreshold) ?: nearestAttribution(acc, SpeakerRecognition.registry, verdictFallbackMin) when { - pre != null && pre == active -> - Log.i(TAG, tag + "-CANCEL-LATE known=$pre(active) revert-via-verdict") + pre != null && pre == active -> { + Log.i(TAG, tag + "-CANCEL-LATE known=$pre(active) active-heal") + // 主动善后:不再被动等句子结束(长句让错标挂几十秒),立即撤销 + uiHandler.post { revertOnsetSwitch(label, pre) } + } pre != null -> Log.i(TAG, tag + "-RECHECK known=$pre label=$label (verdict migrates)") 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 { // 新行出现即封掉之前的开放行,并把其中"未定格的进行中句子"整体迁移 // 过来 —— 硬保证 in-progress 只可能发生在最底行(修复:声纹判定搬家后, diff --git a/app/src/main/java/com/k2fsa/sherpa/onnx/SpeakerEmbedding.kt b/app/src/main/java/com/k2fsa/sherpa/onnx/SpeakerEmbedding.kt index af3a1fa..270c2cf 100644 --- a/app/src/main/java/com/k2fsa/sherpa/onnx/SpeakerEmbedding.kt +++ b/app/src/main/java/com/k2fsa/sherpa/onnx/SpeakerEmbedding.kt @@ -99,6 +99,12 @@ class SpeakerRegistry { 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. @Synchronized fun addNamed(name: String, e: FloatArray) {