# 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`: ```kotlin val dumpMarker = File(filesDir, "stt_dump.marker") val dumpPcm = if (dumpMarker.exists()) File(filesDir, "stt_phone_dump.raw") else null ``` (посмотреть по факту, какой context/filesDir в области видимости). - Лог: "stt" — "PCM-дампа (телефон): ". ### 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) — «убожество» воспроизводимо в оффлайне?