stt: debug-дампы аудио-пути (очки: сырой микрофон в WAV; телефон: принятые PCM) по старту от stt_dump.marker

This commit is contained in:
2026-08-24 05:58:59 +03:00
parent c1c169036a
commit 22eb83fd17
5 changed files with 203 additions and 0 deletions
@@ -366,6 +366,11 @@ class PhoneApp : Application() {
tokensPath = tokens.absolutePath,
numThreads = 4,
)
val dumpPcm = if (File(filesDir, "stt_dump.marker").exists()) {
File(filesDir, "stt_dump_phone.raw")
} else {
null
}
val s = SttStreamer(
stt = whisper,
vad = SherpaStreamingVad(vadModel.absolutePath),
@@ -378,8 +383,10 @@ class PhoneApp : Application() {
log("stt", "тишина 30 с — автo-отмена")
scope.launch { server.hub.broadcast(SttCancel(reason = "timeout")) }
},
dumpPcm = dumpPcm,
)
sttStreamer = s
log("stt", "PCM-дампа (телефон): ${dumpPcm?.absolutePath ?: "off"}")
log("stt", "SttStreamer готов (Whisper-small int8, VAD fp32)")
return s
}
@@ -1,10 +1,13 @@
package pw.binom.viewmate.phone.stt
import java.io.File
import java.io.FileOutputStream
import java.util.concurrent.Executors
import java.util.concurrent.Future
import java.util.concurrent.TimeUnit
import kotlin.concurrent.thread
import kotlin.concurrent.withLock
import pw.binom.viewmate.phone.log
/** s16le (16 кГц, mono) → FloatArray [-1, 1]. Чистая функция — покрыта тестами. */
internal fun s16leToFloat(pcm: ByteArray): FloatArray {
@@ -37,6 +40,7 @@ class SttStreamer(
private val vad: StreamingVad,
private val onPhrase: (phrase: String, full: String) -> Unit,
private val onSilence30s: () -> Unit,
private val dumpPcm: File? = null,
) {
companion object {
private const val SILENCE_TIMEOUT_MS = 30_000L
@@ -60,6 +64,18 @@ class SttStreamer(
@Volatile
private var deadlineMs = 0L
/**
* Debug-дампа принятых PCM: сырые s16le (16 кГц, mono), без WAV-заголовка —
* формат помечен в логе. Сбой открытия не ломает распознавание.
*/
private val dumpOut: FileOutputStream? = dumpPcm?.let { file ->
runCatching {
FileOutputStream(file, true).also {
log("stt", "PCM-дампа: ${file.absolutePath} (16 кГц, s16, mono)")
}
}.onFailure { log("stt", "PCM-дампа: ${it.message}") }.getOrNull()
}
/** Недозаполненные сэмплы (< 512) для следующего окна VAD. */
private var pending = FloatArray(0)
@@ -109,6 +125,11 @@ class SttStreamer(
}
if (i < floats.size) pending = floats.copyOfRange(i, floats.size)
}
val out = dumpOut
if (out != null) {
runCatching { out.write(pcm) }
.onFailure { log("stt", "PCM-дампа: ${it.message}") }
}
}
/**
@@ -141,6 +162,8 @@ class SttStreamer(
deadlineMs = 0
vad.close()
}
runCatching { dumpOut?.close() }
.onFailure { log("stt", "PCM-дампа: ${it.message}") }
executor.shutdownNow()
watchdog.interrupt()
}