5.4 KiB
TASK: debug-дампы аудио-пути очки→телефон (PCM-записи для диагностики STT-качества)
Подозреваемые (по фактам разведки)
(a) убогий микрофон очков (AudioRecord MIC 16 кГц s16 mono, buf 6400, чтение 3200 Б = 100 мс);
(b) транспорт: PCM идёт как base64 в JSON-строке (SttAudio.data → kotlinx-serialization
default ByteArray-сериализатор = base64) по NEWLINE-каналу SPP/WiFi (readLines/readLine);
100-мс-чанк 3200 Б s16 → ~4300 Б base64+JSON. Потери/задержки/перепутанный порядок кусков
по шкатулке — верный кандидат «убожества»;
(c) VAD/Whisper на телефоне (настройки: VAD 0.5/0.25с/0.25с/20с, Whisper-small int8, CPU, 4
потока — на старом HONOR CPU-распознавание медленное и может «не успевать»).
Цель
Запись сырого PCM в двух точках (очки: ДО отправки; телефон: ПОСЛЕ приёма и декода) + сравнение: объёмы/RMS/порядок. Дальше (утром, агент+user): снять с устройств, снять офлайн-ASR-картину, решить (a)/(b)/(c).
Оковы
- Пути только относительные от корня репо /root/WORK/view-mate. Никаких /tmp.
- НЕ ТРОГАТЬ: HostToGlasses.kt, GlassesServer.kt, HostConnection.kt, MainActivity.kt (оба модуля).
- Правим: SttMicStream.kt (очки), SttStreamer.kt (телефон), PhoneApp.kt (телефон, НЕ охраняется), новый WavWriter.kt (очки).
Изменения
1. app-glasses/src/main/kotlin/pw/binom/viewmate/glasses/stt/WavWriter.kt (новый)
Минимальный WAV-райтер (PCM s16le, 16000 Hz, mono):
class WavWriter(path: File, sampleRate: Int = 16000, channels: Int = 1)fun write(pcm: ByteArray)— appends (header пишется один раз при создании файла).close(). Стандартный RIFF/PCM header 44 байта.
2. app-glasses: SttMicStream.kt
- В
start(filesDir, shouldRun): перед запуском потока проверить маркерFile(filesDir, "stt_dump.marker"). Если есть:- создать
File(filesDir, "stt_dump_" + java.time формат HHmmss + ".wav")через WavWriter; - ВСЕ чанки, уходящие в onChunk (только ветка ЖИВОГО микрофона, эмуляторную не трогаем), дописывать в WavWriter;
- log("stt", "dump микрофона: ").
- создать
- При
stop()— закрыть WavWriter (runCatching). - Все операции с dump — runCatching, сбои — только log, поток звука не прерывать.
3. app-phone: SttStreamer.kt
- Конструктор: добавить опциональный параметр
dumpPcmTo: File? = null(хвост конструктора, чтобы не сломать существующие вызовы). - В
accept(pcm): еслиdumpPcmTo != null— дописатьpcmв файл (DataOutputStream/FileOutputStream append, raw s16le, header НЕ нужен — файл помечен как 16кГц s16 mono в логе). Все runCatching. - В
close(): закрыть поток дампа. - Лог: "stt" логгер — "PCM-дампа: ".
4. app-phone: PhoneApp.kt (ensureStt(), ~строка 368)
- При создании SttStreamer передать
dumpPcmTo:(посмотреть по факту, какой context/filesDir в области видимости).val dumpMarker = File(filesDir, "stt_dump.marker") val dumpPcm = if (dumpMarker.exists()) File(filesDir, "stt_phone_dump.raw") else null - Лог: "stt" — "PCM-дампа (телефон): <path или off>".
5. Прогон
./gradlew :app-glasses:testDebugUnitTest :app-phone:testDebugUnitTest :lib-core:jvmTest— зелёные../gradlew :app-glasses:assembleDebug :app-phone:assembleDebug— BUILD SUCCESSFUL.
6. Коммит
git add -A && git commit -m "stt: debug-дампы аудио-пути (очки: сырой микрофон в WAV; телефон: принятые PCM) — старт по stt_dump.marker"- PUSH не делать.
Для утренней диагностики (агент + user)
- На очках:
touchмаркера (adb shell run-as или в самом каталоге /sdcard/Android/data/.../files/stt_dump.marker — агент уточнит path). - На телефоне: аналогично.
- Пользователь произносит фразы в очки → очки делают dump; после сессии — adb pull обоих файлов.
- Сравнение: (1) объём/продолжительность (должны совпадать с точностью на куски), (2) RMS/клиппинг в очках-файле (диагноз (a)), (3) побайтовое/почанковое сравнение очков↔телефон (диагноз (b)), (4) запустить обе записи через серверный ASR (88.125:8006) — «убожество» воспроизводимо в оффлайне?