From 206037994f65850415317d9207726189eb85630d Mon Sep 17 00:00:00 2001 From: liwenhao2 Date: Wed, 16 Sep 2026 11:05:29 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=BA=E5=88=87=E6=8D=A2=E8=A1=8C=E5=93=91?= =?UTF-8?q?=E7=81=AB=E6=A0=B9=E6=B2=BB(=E5=AE=9E=E6=9C=BA=E7=97=85?= =?UTF-8?q?=E7=81=B6):=E5=B0=81=E8=A1=8C=E5=8A=A8=E4=BD=9C=E8=A2=AB?= =?UTF-8?q?=E4=B8=89=E5=A4=84=E6=97=A9=E9=80=80=E5=90=9E=E6=8E=89=E2=80=94?= =?UTF-8?q?=E2=80=94=E2=91=A0=E5=BC=BA=E5=88=87=E6=97=B6=E6=97=A0=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E4=B8=AD=E5=8F=A5(owner=3D-1),=E5=BC=BA=E5=88=B6endpo?= =?UTF-8?q?int=E8=90=BD=E8=BF=9B=E6=97=A0=E5=8F=A5=E5=88=86=E6=94=AF?= =?UTF-8?q?=E5=8F=AA=E6=9C=89resetBuffer,=E6=B0=B8=E6=97=A0finalize;?= =?UTF-8?q?=E2=91=A1finalizeSentence=E4=B8=A4=E4=B8=AA=E6=97=A9=E9=80=80(s?= =?UTF-8?q?entenceById=E7=BC=BA=E5=A4=B1/=E9=87=8D=E5=A4=8D=E5=AE=9A?= =?UTF-8?q?=E6=A0=BC)=E5=9C=A8=E5=B0=81=E8=A1=8C=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E4=B9=8B=E5=89=8D=E3=80=82=E4=BF=AE:=E5=B0=81=E8=A1=8C?= =?UTF-8?q?=E7=A7=BB=E8=87=B3finalizeSentence=E5=85=A5=E5=8F=A3(=E6=97=A9?= =?UTF-8?q?=E9=80=80=E5=89=8D);=E6=97=A0=E5=8F=A5=E5=88=86=E6=94=AF?= =?UTF-8?q?=E8=A1=A5=E5=8F=91closeOnsetLineIfPending;=E9=A2=84=E7=BD=AE?= =?UTF-8?q?=E6=A0=87=E7=AD=BE=E5=8A=A05s=E4=BF=9D=E9=B2=9C=E6=9C=9F?= =?UTF-8?q?=E9=98=B2=E8=AF=AF=E6=8C=82;=E4=BF=AE=E5=A4=8D=E5=90=8E?= =?UTF-8?q?=E9=93=BE=E8=B7=AF:=E5=BC=BA=E5=88=87=E2=86=92=E5=B0=81?= =?UTF-8?q?=E8=A1=8C=E2=86=92=E6=96=B0=E5=8F=A5=E6=96=B0=E8=A1=8C=E6=8C=82?= =?UTF-8?q?S1,=E6=97=A5=E5=BF=97ONSET-CLOSE/ONSET-LINE=E5=8F=AF=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/k2fsa/sherpa/onnx/MainActivity.kt | 52 +++++++++++++++---- 1 file changed, 41 insertions(+), 11 deletions(-) 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 dbedd5f..d2ba9ea 100644 --- a/app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt +++ b/app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt @@ -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() }