强切换行哑火根治(实机病灶):封行动作被三处早退吞掉——①强切时无进行中句(owner=-1),强制endpoint落进无句分支只有resetBuffer,永无finalize;②finalizeSentence两个早退(sentenceById缺失/重复定格)在封行代码之前。修:封行移至finalizeSentence入口(早退前);无句分支补发closeOnsetLineIfPending;预置标签加5s保鲜期防误挂;修复后链路:强切→封行→新句新行挂S1,日志ONSET-CLOSE/ONSET-LINE可查

This commit is contained in:
liwenhao2 2026-09-16 11:05:29 +08:00
parent 6239f0a37e
commit 206037994f
1 changed files with 41 additions and 11 deletions

View File

@ -221,6 +221,7 @@ class MainActivity : AppCompatActivity() {
private val onsetMinSamples = 19200 // 1.2s 开口窗
@Volatile private var onsetSeq = 0L // 行切换/会话开始作废在途开口判定
private var pendingOnsetLabel: String? = null // 主线程:下一新行预置标签
private var pendingOnsetLabelAt = 0L // 预置标签时间戳(过期作废)
private var onsetClosePending = false // 主线程:强切后旧句定格即封底行
@Volatile private var rollingLowHits = 0
// 无缝插话确证(2026-09-15):SUSPECT 等停顿会被"对方话没停就接话"憋死
@ -851,9 +852,11 @@ class MainActivity : AppCompatActivity() {
rmsOf(samples) else 0f) + ")")
switchSuspicionAtMs = 0L
}
var forcedEp = false
if (fastEp) {
val forced = pendingForceEndpoint
pendingForceEndpoint = false
forcedEp = forced
rollingLowStreak = 0
switchSuspicionAtMs = 0L
silenceRun = 0
@ -905,7 +908,13 @@ class MainActivity : AppCompatActivity() {
finalizeSentence(sid, accepted)
}
} else {
// 强切但无句可定格(owner=-1):封行/预置标签仍在
// 主线程挂着,finalizeSentence 永不来的这条路径上
// 补发封行,否则换行哑火(2026-09-16 现场根因)
resetBuffer()
if (forcedEp) {
uiHandler.post { closeOnsetLineIfPending() }
}
}
}
}
@ -1618,6 +1627,7 @@ class MainActivity : AppCompatActivity() {
val list = if (historyMode) stashedLiveLines ?: subtitleLines else subtitleLines
val bottom = list.lastOrNull() ?: return
pendingOnsetLabel = label
pendingOnsetLabelAt = SystemClock.elapsedRealtime()
if (!bottom.open) return // 底行已封:下一行自然消费预置标签
val busy = bottom.sentences.any { !it.finalized }
if (busy) {
@ -1628,6 +1638,14 @@ class MainActivity : AppCompatActivity() {
}
}
/** 强切落进"无句可定格"分支时的封行兜底(主线程);预置标签保留等待下一行 */
private fun closeOnsetLineIfPending() {
if (!onsetClosePending) return
onsetClosePending = false
subtitleLines.lastOrNull()?.takeIf { it.open }?.open = false
Log.i(TAG, "ONSET-CLOSE no-sentence forced endpoint")
}
private fun appendNewLine(label: String?): SubtitleLine {
// 新行出现即封掉之前的开放行,并把其中"未定格的进行中句子"整体迁移
// 过来 —— 硬保证 in-progress 只可能发生在最底行(修复:声纹判定搬家后,
@ -1642,12 +1660,23 @@ class MainActivity : AppCompatActivity() {
old?.sentences?.removeAll(migrating.toSet())
}
// 开口即查预置标签(ONSET-SWITCH 判了新说话人)→ 新行直接采用,
// 并成为"最近确认说话人"(后续空闲新行沿用);不等 endpoint 权威判定
val eff = pendingOnsetLabel ?: label
if (pendingOnsetLabel != null) {
// 并成为"最近确认说话人"(后续空闲新行沿用);不等 endpoint 权威判定。
// 超 5s 未消费 = 强切未落地(如无句可定格且无后续新句),作废防误挂
val preset = pendingOnsetLabel
val eff: String?
if (preset != null &&
SystemClock.elapsedRealtime() - pendingOnsetLabelAt < 5_000L
) {
pendingOnsetLabel = null
lastConfirmedLabel = eff
Log.i(TAG, "ONSET-LINE new line label=$eff")
lastConfirmedLabel = preset
eff = preset
Log.i(TAG, "ONSET-LINE new line label=$preset")
} else {
if (preset != null) {
pendingOnsetLabel = null
Log.i(TAG, "ONSET-LINE stale preset dropped")
}
eff = label
}
val line = SubtitleLine(
id = lineIdSeq.incrementAndGet(),
@ -1700,6 +1729,13 @@ class MainActivity : AppCompatActivity() {
// endpoint/强制断句的 settle:该句就地定格(归属修正走 confirmSpeaker,
// v5 起声纹判定已移出录音线程)。
private fun finalizeSentence(sid: Long, willRefine: Boolean) {
// 开口即查/强切封行:必须在任何早退之前执行——sentenceById 缺失或
// 重复定格都会从下面两行 return,尾部执行曾让封行永哑
// (2026-09-16 现场:强切 owner=sid-1,强制 endpoint 无句可定格,换行丢失)
if (onsetClosePending) {
onsetClosePending = false
subtitleLines.lastOrNull()?.takeIf { it.open }?.open = false
}
lastSettledSid = sid
lastSentenceSettledAt = SystemClock.elapsedRealtime()
val sentence = sentenceById[sid] ?: return
@ -1720,12 +1756,6 @@ class MainActivity : AppCompatActivity() {
// (SKIP_SHORT 等情况 submitRefineTask 已写对账日志,不重复记)
sentenceById.remove(sid)
}
// 开口即查强切:旧句已定格,此刻封底行,让新说话人首句另起带
// 预置标签的新行(由后续 onPartial 尾部创建)
if (onsetClosePending) {
onsetClosePending = false
subtitleLines.lastOrNull()?.takeIf { it.open }?.open = false
}
sentence.line?.let { notifyLine(it) }
autoScroll()
}