MIC-ASR-SEARCH.md: свои готовые библиотеки mic-kmp/asr-kmp/vad-kmp вместо кода микрофона из assistent
This commit is contained in:
+16
-14
@@ -231,25 +231,24 @@ clipboardManager.setText(AnnotatedString(segment.code))
|
|||||||
(`bold`/`italic`/`code`/`strikethrough`/`link`). Логику стилей держат в данных, а не
|
(`bold`/`italic`/`code`/`strikethrough`/`link`). Логику стилей держат в данных, а не
|
||||||
в Композе — удобно и переносимо.
|
в Композе — удобно и переносимо.
|
||||||
|
|
||||||
## 4. Запись микрофона на десктопе — без единой библиотеки
|
## 4. Запись микрофона — код из assistent брать НЕ надо
|
||||||
|
|
||||||
**Где:** `client/src/main/kotlin/pw/binom/client/audio/MicrophoneRecorder.kt` (83 строки).
|
**Где:** `client/src/main/kotlin/pw/binom/client/audio/MicrophoneRecorder.kt` (83 строки).
|
||||||
|
Всё на стандартной Java (`javax.sound.sampled`: `AudioFormat`, `AudioSystem`,
|
||||||
|
`DataLine`, `TargetDataLine`), формат PCM 16 бит / 16 кГц / моно, буфер 4096 байт.
|
||||||
|
|
||||||
Ценнейшая находка для нашей кнопки записи. Всё на стандартной Java:
|
**Но копировать это не нужно.** У нас есть своя готовая библиотека **`mic-kmp`**
|
||||||
|
(опубликована, версия `1.0.0`), которая делает то же самое, только лучше:
|
||||||
|
отмена структурная — микрофон освобождается сам через `finally`, без ручного `close()`.
|
||||||
|
|
||||||
```kotlin
|
Здесь файл полезен **только как источник приёмов**:
|
||||||
import javax.sound.sampled.AudioFormat
|
|
||||||
import javax.sound.sampled.AudioSystem
|
|
||||||
import javax.sound.sampled.DataLine
|
|
||||||
import javax.sound.sampled.TargetDataLine
|
|
||||||
```
|
|
||||||
|
|
||||||
Формат: **PCM 16 бит, 16 кГц, моно, little-endian**, буфер 4096 байт.
|
- подтверждает формат, который ждёт распознавание: **16 кГц, моно, PCM 16 бит, little-endian**
|
||||||
Наружу отдаёт `ReceiveChannel<ByteArray>` — чанки сразу уходят на распознавание,
|
(`AudioFormat.Encoding.PCM_SIGNED, sampleRate, 16, 1, 2, sampleRate, false`);
|
||||||
без накопления файла. Микрофон — `AudioSystem.getLine(...) as TargetDataLine`.
|
- показывает, что сторонние библиотеки для звука не нужны — хватает стандартной Java;
|
||||||
|
- отдаёт данные кусками (`ReceiveChannel<ByteArray>`), а не файлом — можно стримить.
|
||||||
|
|
||||||
**Почему важно:** сторонние библиотеки для звука не нужны вообще. Мы это
|
Подробности и координаты библиотек — `MIC-ASR-SEARCH.md`.
|
||||||
собирались решать позже, но приём уже есть и он рабочий.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -295,5 +294,8 @@ import javax.sound.sampled.TargetDataLine
|
|||||||
| Отрисовку Markdown (4 файла) | Экраны целиком (`ChatScreen`, `SettingsDialog`, …) |
|
| Отрисовку Markdown (4 файла) | Экраны целиком (`ChatScreen`, `SettingsDialog`, …) |
|
||||||
| Приёмы: таблицы, ленивый список, автоскролл, полоса прокрутки | Модули `chat-*` и модели сообщений |
|
| Приёмы: таблицы, ленивый список, автоскролл, полоса прокрутки | Модули `chat-*` и модели сообщений |
|
||||||
| Кликабельные ссылки, копирование кода, выделение текста | `MessageBubble` |
|
| Кликабельные ссылки, копирование кода, выделение текста | `MessageBubble` |
|
||||||
| Запись микрофона через `javax.sound` | `ChatViewModel`, `config/*`, `cache/*` |
|
| Приём записи звука (формат 16к/моно, `javax.sound`) | `ChatViewModel`, `config/*`, `cache/*` |
|
||||||
| Зависимость `ru.otpbank.ai:markdown:0.51.0` | Чужой дизайн и окна |
|
| Зависимость `ru.otpbank.ai:markdown:0.51.0` | Чужой дизайн и окна |
|
||||||
|
|
||||||
|
**Микрофон и распознавание — не из assistent.** У нас для этого есть свои
|
||||||
|
опубликованные библиотеки: `mic-kmp`, `asr-kmp`, `vad-kmp`. Разбор — `MIC-ASR-SEARCH.md`.
|
||||||
|
|||||||
@@ -0,0 +1,174 @@
|
|||||||
|
# Микрофон и распознавание: результаты поиска
|
||||||
|
|
||||||
|
**Задача была:** найти готовые библиотеки для работы с микрофоном и распознавания,
|
||||||
|
прежде чем брать код из `ai/assistent`. **Код не пишем** — только поиск.
|
||||||
|
|
||||||
|
**Ответ короткий:** и то, и другое **уже есть у нас, готовое и опубликованное**.
|
||||||
|
Из `ai/assistent` брать код микрофона **не нужно вообще**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Микрофон: библиотека есть — `mic-kmp`
|
||||||
|
|
||||||
|
**Репозиторий:** https://git.binom.pw/subochev/mic-kmp
|
||||||
|
**Каталог:** https://git.binom.pw/subochev/libraries (раздел «📦 Микрофон»)
|
||||||
|
|
||||||
|
KMP-библиотека: поток микрофона как `Flow<ByteArray>` (s16le mono PCM).
|
||||||
|
Три файла, вот и вся библиотека:
|
||||||
|
|
||||||
|
| Файл | Что делает |
|
||||||
|
|---|---|
|
||||||
|
| `mic-api/src/commonMain/.../MicAudio.kt` | интерфейс `MicDevice` + объект `MicAudio.record(...)` |
|
||||||
|
| `mic-api/src/jvmMain/.../MicJvm.kt` | JVM — `javax.sound.sampled` (`TargetDataLine`) |
|
||||||
|
| `mic-api/src/androidMain/.../MicAndroid.kt` | Android — `AudioRecord` |
|
||||||
|
|
||||||
|
**Таргеты: `jvm` и `android`.** JVM-таргет — ровно то, что нужно десктопу.
|
||||||
|
JVM-код собран на `JvmTarget.JVM_17`.
|
||||||
|
|
||||||
|
**Опубликована в Nexus `caffeine`, версия `1.0.0`:**
|
||||||
|
- `pw.binom.mic:mic-api:1.0.0` (KMP metadata)
|
||||||
|
- `pw.binom.mic:mic-api-jvm:1.0.0` (JVM — **наше**)
|
||||||
|
- `pw.binom.mic:mic-api-android:1.0.0`
|
||||||
|
|
||||||
|
Проверено: `maven-metadata.xml` отдаёт `HTTP 200`, latest = `1.0.0`.
|
||||||
|
|
||||||
|
**Использование — одна строка:**
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
MicAudio.record(sampleRate = 16000, channels = 1, chunkMillis = 100)
|
||||||
|
.onEach { chunk: ByteArray -> /* s16le mono PCM, ~100 мс */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Чем лучше кода из assistent:** отмена **структурная**. Остановил корутину-коллектор —
|
||||||
|
микрофон освободился сам, в `finally`. Не надо помнить про `close()`. В assistent
|
||||||
|
(`MicrophoneRecorder`, 83 строки) — ручное управление через `start()`/`stop()`,
|
||||||
|
и связка «канал + флаг recording + внешний TargetDataLine». Наш вариант чище.
|
||||||
|
|
||||||
|
**Вывод: копировать `MicrophoneRecorder` из assistent НЕ надо — подключить `mic-kmp`.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Распознавание: библиотека есть — `asr-kmp`
|
||||||
|
|
||||||
|
**Репозиторий:** https://git.binom.pw/subochev/asr-kmp
|
||||||
|
**Каталог:** https://git.binom.pw/subochev/libraries (раздел «🎙 ASR»)
|
||||||
|
|
||||||
|
KMP, таргеты **jvm** и **android** (плюс native: linux, macos, mingw).
|
||||||
|
Потоковый интерфейс — приём аудио кусками и **промежуточный текст по ходу**:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
interface Stt : AutoCloseable {
|
||||||
|
fun feed(samples: FloatArray) // порция 16 кГц моно PCM, [-1, 1]
|
||||||
|
fun partialText(): String? // промежуточный текст (растёт по ходу)
|
||||||
|
fun endOfInput() // конец ввода
|
||||||
|
fun finalText(): String // итог
|
||||||
|
override fun close()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Есть `partialText` — текст появляется по мере речи**, а не после конца записи.
|
||||||
|
Для нашей кнопки микрофона это именно то поведение, которое нужно.
|
||||||
|
|
||||||
|
### Движки (все опубликованы в `caffeine`)
|
||||||
|
|
||||||
|
| Движок | Координаты (группа `pw.binom.asr`) | Версии в Nexus |
|
||||||
|
|---|---|---|
|
||||||
|
| `asr-api` — интерфейс | `asr-api` / `-jvm` / `-android` | до `4` |
|
||||||
|
| `asr-vosk` — Vosk | `asr-vosk` / `-jvm` / `-android` | есть |
|
||||||
|
| `asr-qwen3` — Qwen3-ASR | `asr-qwen3` / `-jvm` | есть |
|
||||||
|
| `asr-whisper` — Whisper | `asr-whisper` / `-jvm` / `-android` | есть |
|
||||||
|
| `asr-openai` — удалённое (OpenAI-совместимое) | `asr-openai` | есть |
|
||||||
|
|
||||||
|
Под капотом нейронок — `sherpa-onnx` (`com.github.k2-fsa.sherpa-onnx`, `v1.13.6`),
|
||||||
|
для JVM отдельно тянется нативная библиотека `sherpa-onnx-native-lib-linux-x64`.
|
||||||
|
|
||||||
|
### Про модели (важно)
|
||||||
|
|
||||||
|
- **Qwen3-ASR** — модель уже на нашем статик-сервере, качать из-за бугра не надо:
|
||||||
|
`http://static.binom.pw/models/qwen3_06/` (~940 МБ: encoder 175M, decoder 721M, frontend 43M).
|
||||||
|
- **Whisper small int8** — модели на статике НЕТ, качать самостоятельно.
|
||||||
|
- **Vosk** (`vosk-model-small-ru-0.22`) — **входит в комплект**, распаковывается сам.
|
||||||
|
Самый простой путь для первого запуска: ничего качать не надо.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Рядом есть ещё три полезные библиотеки
|
||||||
|
|
||||||
|
Из того же каталога `subochev/libraries`:
|
||||||
|
|
||||||
|
### VAD — детектор речи, `vad-kmp`
|
||||||
|
https://git.binom.pw/subochev/vad-kmp — «VAD — Silero», KMP jvm+android,
|
||||||
|
**модель внутри артефакта** (2.3 МБ, ничего качать не надо).
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
VadInstance(16000).use { vad ->
|
||||||
|
val prob = vad.processingWindow(windowPcmFloats) // FloatArray → [0..1]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Координаты: `pw.binom.voice.vad:vad-jvm`, версия `2`.
|
||||||
|
**Зачем:** определять, говорит человек или молчит — не гнать тишину в распознавание.
|
||||||
|
|
||||||
|
### Есть и второй VAD
|
||||||
|
`pw.binom.ai.voice:vad` — «детектор речи (VAD) — `vad` / `vad-jvm`». Два разных VAD,
|
||||||
|
при выборе разобраться, какой свежее.
|
||||||
|
|
||||||
|
### OpenAI-клиент
|
||||||
|
`pw.binom.openai:api` / `api-jvm` + `ktor-impl` (HTTP-движок). Нужен для `asr-openai`
|
||||||
|
(распознавание на удалённом сервере, а не локальной нейронкой).
|
||||||
|
|
||||||
|
### Speaker-эмбеддинги и text-embedding
|
||||||
|
- `subochev/embedding-kmp` — voice embedding (кто говорит).
|
||||||
|
- `subochev/text-embedding-kmp` — text embedding (SigLIP2).
|
||||||
|
Нам сейчас не нужно, но пусть будет известно.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Как это собрано вместе — пример `view-mate`
|
||||||
|
|
||||||
|
**Репо:** `/root/WORK/view-mate` (очки RayNeo X2), модули `app-glasses`, `app-phone`, `app-host`, `lib-core`.
|
||||||
|
|
||||||
|
**Важно:** view-mate **не подключает** `mic-kmp`/`asr-kmp` как библиотеки — у него
|
||||||
|
своя реализация `SttMicStream` на `android.media.AudioRecord`
|
||||||
|
(`app-glasses/.../stt/SttMicStream.kt`). То есть `mic-kmp` — это **вынесенная
|
||||||
|
и обобщённая** версия того же подхода.
|
||||||
|
|
||||||
|
Из полезного в `SttMicStream.kt` — комментарий на строке 229:
|
||||||
|
`AudioRecord.read(byte[], int, inFrameCount)` возвращает **число кадров**, а не байт.
|
||||||
|
Классические грабли Android-записи.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Чем пользуется сам `ai/assistent`
|
||||||
|
|
||||||
|
Важно для понимания разницы подходов:
|
||||||
|
|
||||||
|
`chat-server/.../stt/SttClientImpl.kt` — это **не** локальное распознавание.
|
||||||
|
Это HTTP-клиент к OpenAI-совместимому API:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
client.post("$base/audio/transcriptions") { ... } // multipart, как у OpenAI
|
||||||
|
```
|
||||||
|
|
||||||
|
Настройки через переменные окружения:
|
||||||
|
- `STT_API_KEY` — без него голосовой адаптер выключен;
|
||||||
|
- `STT_URL` — по умолчанию `https://api.openai.com/v1`;
|
||||||
|
- `STT_MODEL` — по умолчанию `whisper-1`.
|
||||||
|
|
||||||
|
**Вывод:** у assistent распознавание — **отправка файла на сервер**. У нас
|
||||||
|
`asr-kmp` умеет и локальные нейронки (Qwen3, Whisper, Vosk), и удалённое (`asr-openai`).
|
||||||
|
Наш вариант шире, а `mic-kmp` (поток) + `asr-kmp` (поток) + `vad-kmp` (детектор речи)
|
||||||
|
складываются в готовую цепочку: **микрофон → детектор речи → распознавание → текст**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Итог: что делать
|
||||||
|
|
||||||
|
| Вопрос | Ответ |
|
||||||
|
|---|---|
|
||||||
|
| Та отдельная библиотечка для микрофона, которую мы делали — существует? | **Да.** `mic-kmp`, опубликована в `caffeine` версии `1.0.0`, jvm-таргет есть. |
|
||||||
|
| Библиотечки распознавания с нейронками где? | **`asr-kmp`** — Qwen3, Whisper, Vosk, удалённое. Всё в `caffeine`. Плюс каталог: `subochev/libraries`. |
|
||||||
|
| Есть ли где-то микрофон в каталоге библиотек? | **Да**, первым разделом — «📦 Микрофон». |
|
||||||
|
| Брать ли код микрофона из `ai/assistent`? | **Нет.** Подключаем `mic-kmp` — чище, с автоосвобождением, без внешних библиотек. |
|
||||||
|
| Нужны ли сторонние зависимости для звука? | **Нет.** Всё на стандартной Java (`javax.sound.sampled`) и sherpa-onnx для нейронок. |
|
||||||
|
|
||||||
|
**Код не писан** — только поиск, как и просили.
|
||||||
@@ -17,6 +17,8 @@ sketches/004-settings-agents/index.html # настройки агентов
|
|||||||
|
|
||||||
`BORROW-FROM-ASSISTENT.md` — что берём из `ai/assistent` (приёмы, плагины, версии),
|
`BORROW-FROM-ASSISTENT.md` — что берём из `ai/assistent` (приёмы, плагины, версии),
|
||||||
а что не берём (архитектура, экраны, дизайн — своё).
|
а что не берём (архитектура, экраны, дизайн — своё).
|
||||||
|
`MIC-ASR-SEARCH.md` — библиотеки для микрофона и распознавания: своё готовое
|
||||||
|
(`mic-kmp`, `asr-kmp`, `vad-kmp`), вместо копирования кода из assistent.
|
||||||
`MARKDOWN-SOURCE.md` — откуда брать готовую отрисовку Markdown (файлы и адреса).
|
`MARKDOWN-SOURCE.md` — откуда брать готовую отрисовку Markdown (файлы и адреса).
|
||||||
`REQUIREMENTS.md` — требования. Статус: накидываем, ни один пункт не обязателен
|
`REQUIREMENTS.md` — требования. Статус: накидываем, ни один пункт не обязателен
|
||||||
к исполнению в том виде, как записан.
|
к исполнению в том виде, как записан.
|
||||||
@@ -72,3 +74,4 @@ sketches/004-settings-agents/index.html # настройки агентов
|
|||||||
- **Откуда берётся распознавание речи.** Отдельная тема, к интерфейсу не
|
- **Откуда берётся распознавание речи.** Отдельная тема, к интерфейсу не
|
||||||
относится: в дизайне показано только место кнопки и что происходит на экране
|
относится: в дизайне показано только место кнопки и что происходит на экране
|
||||||
во время записи. Кто именно превращает голос в текст — решается потом.
|
во время записи. Кто именно превращает голос в текст — решается потом.
|
||||||
|
Готовые библиотеки уже найдены, см. `MIC-ASR-SEARCH.md`.
|
||||||
|
|||||||
+5
-5
@@ -62,11 +62,11 @@
|
|||||||
(по нажатию или на удержание) — решим позже.
|
(по нажатию или на удержание) — решим позже.
|
||||||
- **R19.** Распознавание речи — **отдельная тема, здесь не решается.** Важно только:
|
- **R19.** Распознавание речи — **отдельная тема, здесь не решается.** Важно только:
|
||||||
где кнопка, что видно во время записи, что происходит после.
|
где кнопка, что видно во время записи, что происходит после.
|
||||||
- **R19.1.** **Как читать микрофон — уже известно, берём приём из `ai/assistent`.**
|
- **R19.1.** **Как читать микрофон — решено, пишем не мы.** Подключаем свою готовую
|
||||||
Никаких сторонних библиотек: стандартные средства Java, класс
|
библиотеку `mic-kmp` (`pw.binom.mic:mic-api-jvm`, версия `1.0.0`, есть в Nexus).
|
||||||
`MicrophoneRecorder` (83 строки). Формат — 16 кГц, 16 бит, моно.
|
Отдаёт звук потоком кусочков, а не файлом: можно распознавать сразу, не дожидаясь
|
||||||
Наружу отдаёт поток кусочков, а не файл: можно отправлять на распознавание
|
конца записи. Микрофон освобождается сам при остановке. Формат — 16 кГц, моно.
|
||||||
сразу, не дожидаясь конца записи. Подробности — `BORROW-FROM-ASSISTENT.md`, п. 4.
|
Код из `ai/assistent` для этого **не берём**. Подробности — `MIC-ASR-SEARCH.md`.
|
||||||
|
|
||||||
## 6. Несколько агентов
|
## 6. Несколько агентов
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user