9.0 KiB
9.0 KiB
m11-voice-input — план работ (d-dev)
Модуль: VoiceInput — офлайн-STT (Vosk) + грамматика Ф-1.2 + TTS-подтверждения + интеграция в экран «Тренировка». Алгоритм:
std1\_rules\d\d-dev.md. Требования: module_reqs.md. Зависимости: m09 (WorkoutCore/LocalDb/SyncQueue — DONE), m10 (экран «Тренировка», таймер отдыха, «Настройки» — DONE); часы — m12.
| Шаг | Что делаем | Статус |
|---|---|---|
| 0 | Модульные документы: module_reqs.md / readme.md / SKILL.md | ✅ 2026-10-19 |
| 1 | Список единиц (парсер/движок/модель/исполнитель/UI/тесты) | ✅ 2026-10-19 |
| 1.3 | Пользователь! утверждение списка (+ решения по объёму v1) | ✅ 2026-10-19 |
| 2 | Спецификации: api_specs (парсер, движок/модель, исполнитель, UI) | ✅ 2026-10-19 |
| 2.4 | Пользователь! утверждение спецификаций | ✅ 2026-10-19 |
| 3 | Реализация + тесты + сборка | ✅ 2026-10-19 |
| 3.4 | Пользователь! утверждение реализации (закрытие модуля) | ✅ 2026-10-19 |
| 4 | Завершение: план, примечания | ✅ 2026-10-19 |
Статусы: ⬜ не начат · 🔄 в работе · ✅ готов.
Список единиц (шаг 1)
| # | Единица | Что делает |
|---|---|---|
| 1 | VoiceCommandParser (+ NumberRu) |
чистая функция: текст → VoiceCommand (Approach/Weight/Rest/Rpe) или Unknown; числа словами 0–999 и цифрами, «с половиной» → +0,5; диапазоны Ф-1.2; semantics для журнала |
| 2 | SttEngine/VoskEngine |
обёртка Vosk SpeechService: инициализация модели из filesDir, старт/стоп прослушивания, финальные гипотезы в колбэк; опциональный setGrammar (JSON-словарь); интерфейс — для тестов |
| 3 | ModelStore |
загрузка zip модели по Voice.ModelUrl (OkHttp, уже в проекте), распаковка в filesDir, идемпотентность, статус (нет/загружена, МБ) |
| 4 | VoiceCommandExecutor |
применение команды: Approach → logSet(source=voice), Weight → рабочий вес экрана, Rest → старт таймера m10, Rpe → WorkoutCore.setLastSetRpe; журнал logCommand (принято/отклонено 🛡); будит SyncWorker |
| 5 | TtsFeedback |
системный Android TTS (ru-RU) + ToneGenerator (щелчок/сигнал ошибки); деградация без TTS (текст на экране) |
| 6 | UI «Тренировка» | кнопка микрофона (toggle, индикатор «Слушаю…»), runtime-разрешение RECORD_AUDIO, строка последней команды, «текущее упражнение» (тап в блоке плана), рабочий вес от «Вес W» |
| 7 | UI «Настройки» | блок «Распознавание речи»: статус модели, «Загрузить», «Проверить TTS»; Voice.ModelUrl |
| 8 | WorkoutCore.setLastSetRpe |
расширение m09 (errata, как renameExercise в m10): RPE последнего подхода + повторная отправка в sync |
| 9 | unit-тесты | парсер (4 команды × формы чисел, границы, мусор), NumberRu, исполнитель на фейках |
Решения на гейт 1.3 (для владельца)
- Доставка модели Vosk (~45 МБ,
vosk-model-small-ru-0.22): рекомендуем (а) — загрузка zip по https при первом включении микрофона / из «Настроек» (Voice.ModelUrl, умолчание alphacephei.com; можно указать свой сервер), модель живёт вfilesDir, в APK и репозиторий не входит. Альтернативы: (б) вшить в APK assets (+45 МБ и бинарник в git); (в) ручной сайдлоад adb. - Текущее упражнение для голоса — выбор тапом в блоке плана (умолчание — первый пункт плана); голосовой выбор упражнения по имени в v1 отсутствует (имена произвольные, распознавание ненадёжно). Форма ручного ввода m10 сохраняется (правка ≤ 2 касаний, Ф-1.6).
- «РПЭ N» — новый метод
WorkoutCore.setLastSetRpe(расширение m09 по образцуrenameExercise: обновление строки + повторный enqueue; сервер идемпотентен по client_guid). - Грамматика Vosk —
setGrammarприменяется при поддержке моделью; обязательна строгая постобработка парсером (малые модели не гарантируют грамматику; лишнее отклоняется по Ф-1.3). - TTS — системный Android TTS; на устройстве владельца должен быть ru-голос (Google TTS); при отсутствии — деградация до щелчка/сигнала + текст на экране.
Примечания по ходу работ
- Состав реализации (
app/src/main/java/com/watchbody/phone/voice/):VoiceCommandParser.kt(sealed VoiceCommand + NumberRu 0–999 + tokenize с «эр пэ э»→«рпэ» + semantics),VoskResult.kt(разбор JSON Vosk),SttEngine.kt(интерфейс + VoskEngine: грамматика-список слов с откатом на свободный разбор, n-best 3),ModelStore.kt(zip→filesDir, zip-slip, single-root срезка, no-op при готовой модели),VoiceCommandExecutor.kt(VoiceCoreGateway/эффекты/отклонения),TtsFeedback.kt(TTS ru + ToneGenerator BEEP/NACK, деградация). Расширения:WorkoutCore.setLastSetRpe+ DAOlastSet(m09),AppSettings.voiceModelUrl+ валидатор https (m10), контейнер (modelStore/ttsFeedback), WorkoutScreen (Mic-кнопка, RECORD_AUDIO, диалог модели, «текущее упражнение» тапом в плане, строка голоса), SettingsScreen (блок «Распознавание речи»: URL/статус/загрузка/ «Проверить TTS»), манифест RECORD_AUDIO, gradle: vosk-android 0.3.47 + icons-extended + MockWebServer (test). - Сборка/тесты: BUILD SUCCESSFUL; APK 57,3 МБ (рост с 12 МБ — JNI Vosk по 4 ABI + icons-extended; debug без минификации — release c R8/abiFilters сожмёт, кандидат оптимизации); unit 63/63 (m09 18 + m10 18 + m11 27: парсер 9, исполнитель 10, модель+валидатор 8).
- Живой URL модели проверен (2026-10-19): alphacephei.com →
200 OK, 46 236 750 байт (~44 МБ) — умолчание
Voice.ModelUrlрабочее. - Errata API vosk-android 0.3.47 (вскрыты компиляцией, spec 02
скорректирован по факту):
SpeechServiceуправляетсяstop()/shutdown()(stopListening/destroy нет);RecognitionListener— 5 методов, команды приходят вonResult(onFinalResult — при остановке); сигнал ошибки —TONE_PROP_NACK(TONE_PROP_ERR нет). - Решения по ходу: парсер в approach при out_of_range пишет ОБА
значения ({"weight_kg":100.0,"reps":101} — информативнее для разбора
журнала);
syncRequest_executor срабатывает после ЛЮБОЙ журналируемой команды (журнал отклонённых тоже уходит в sync; spec 03 п.8 уточнён по смыслу); инициализация Model Vosk — в Dispatchers.IO (не main). - Не проверено в среде (нет устройства/микрофона): живое распознавание, TTS-озвучка, щелчки/сигналы, загрузка модели на устройстве, точность КУ-1 — критерии 2/4 приёмки на стороне владельца; сетевой контур модели проверен MockWebServer + живым URL.