stt(mic): вынести кадровую арифметику в micChunkBytes + регрессионные тесты MicChunkTest

- CHUNK_BYTES=3200, micChunkBytes(buf, frames): кадры read() -> байты (*2)
- 4 теста: полный чанк 3200 Б без копирования, короткий read,
  документация старого бага (copyOf(кадры) = полка), inFrameCount=1600
This commit is contained in:
2026-08-24 12:23:33 +03:00
parent fc84ba16b6
commit 40edd88c1d
2 changed files with 65 additions and 8 deletions
@@ -58,16 +58,11 @@ class SttMicStream(
try {
recorder.startRecording()
log("stt", "микрофон запущен (16 кГц, s16, mono)")
val buf = ByteArray(3_200) // = 1600 кадров (16-бит моно) = 100 мс
val buf = ByteArray(CHUNK_BYTES) // = 1600 кадров (16-бит моно) = 100 мс
while (isActive && shouldRun()) {
// AudioRecord.read(byte[], int, inFrameCount): 3-й аргумент — В КАДРАХ,
// а НЕ в байтах (16-бит моно: кадр = 2 байта). Просим buf.size/2 кадров
// (= buf.size байт = 100 мс), получаем frames — и переводиМ В БАЙТЫ (×2).
// Старый баг: n (кадры) брался как байты → ушла половина куска.
val frames = recorder.read(buf, 0, buf.size / 2)
val frames = recorder.read(buf, 0, CHUNK_BYTES / 2) // inFrameCount — КАДРЫ
if (frames > 0) {
val bytes = frames * 2
val chunk = if (bytes == buf.size) buf else buf.copyOf(bytes)
val chunk = micChunkBytes(buf, frames)
if (dumpWriter != null) {
runCatching { dumpWriter.write(chunk) }
.onFailure { log("stt", "dump микрофона: ${it.message}") }
@@ -143,6 +138,20 @@ class SttMicStream(
}
}
/** 100 мс при 16 кГц, s16, моно: 1600 кадров × 2 байта = 3200 байт. */
internal const val CHUNK_BYTES = 3_200
/**
* AudioRecord.read(byte[], int, inFrameCount) возвращает ЧИСЛО КАДРОВ, а не байт.
* 16-бит моно: кадр = 2 байта. Байты = frames * 2.
* Старый баг (заделан в c940a7e): return read() (кадры) сравнивали с buf.size (байты)
* — всегда ложь → copyOf(n) резал кусок пополам (терялись 2-е 50 мс каждого 100 мс).
*/
internal fun micChunkBytes(buf: ByteArray, frames: Int): ByteArray {
val bytes = frames * 2
return if (bytes == buf.size) buf else buf.copyOf(bytes)
}
/** FloatArray [-1, 1] → s16le ByteArray (16 кГц, mono). */
internal fun floatSamplesToPcm16(samples: FloatArray): ByteArray {
val out = ByteArray(samples.size * 2)