Задача звучала просто: дать покупателю возможность из любого браузера и с любого устройства продиктовать поисковый запрос словами. Голос превращается в текст в строке поиска, а отдельные слова работают как команды — «искать», «отменить» — и запускают соответствующие действия на сайте. Ниже разбираем, как такая система устроена изнутри: от микрофона в браузере до ответа службы распознавания и обратно.
Задача
Нужно разработать функцию распознавания речи на веб-странице интернет-магазина. Покупатель нажимает кнопку записи, диктует запрос — и видит его текстом в поисковой строке. Часть сказанного обрабатывается как команды: «искать» запускает поиск, «отменить» сбрасывает введённое.
Ключевое требование — полная совместимость с самыми распространёнными браузерами: Chrome, Firefox, Safari, Opera и Edge. То есть решение не может опираться на возможности, которые есть только в одном из них.
Что должна уметь система
Чтобы это заработало, нужны три вещи:
- получить аудиопоток из браузера;
- передать аудиоданные потоком в службу распознавания речи;
- получать результаты в режиме реального времени — пока пользователь ещё говорит.
Шаг 1. Как получить аудиопоток из браузера
Здесь нам поможет API getUserMedia / Stream, который поддерживает большинство популярных браузеров. Через этот браузерный интерфейс мы получаем аудиопоток с микрофона и отправляем его в нашу службу распознавания речи.

Дальше инициализируем аудиоконтекст и вызываем getUserMedia. Хотя такая конфигурация совместима с основными браузерами, параметры, которые передают в этот API, могут отличаться в зависимости от потребностей — детали стоит сверять с официальной документацией.
Настройка аудиоконтекста
Конфигурация аудиоконтекста и процессора сценариев тоже зависит от задачи. Сложность в том, что некоторые комбинации параметров сейчас не работают в Safari — это стоит проверять отдельно.
Отдельное внимание нужно уделить подписке на событие «audioprocess»: для потока с микрофона оно фактически выполняет роль события «ondata».
Звук регистрируется в стерео, поэтому с микрофона идут два аудиоканала. На самом деле мы забираем только один из них, потому что службе распознавания нужно аудио с одним каналом. Но в зависимости от ваших потребностей со звуком можно работать как угодно.
Что делать при остановке записи
- Выключить микрофон. Прекратить использование микрофона браузера и сбросить переменные, если они есть.
- Отключить слушателя события аудиопроцесса. Без этого поток и дальше будет вызывать событие, даже когда микрофон уже деактивирован.
Шаг 2. Куда передавать аудио
Самый простой путь — воспользоваться встроенным в браузер API распознавания речи. С ним веб-приложение распознаёт речь потоком прямо в браузере, без каких-либо сторонних сервисов.
Но, как и в каждом интересном нововведении, здесь есть мелкие проблемы совместимости. И именно они делают этот подход совершенно непригодным для продакшн-приложений.

Для хорошего пользовательского опыта нам нужна система распознавания в реальном времени, которая возвращает результаты, пока пользователь говорит, а не записывает звук полностью и только потом его транскрибирует. Значит, нужен сторонний сервис со следующими свойствами:
Потоковое распознавание
Результат должен приходить непрерывно, по мере того как человек говорит, а не одним куском после завершения записи.
Совместимость с каждым браузером
Сервис не должен зависеть от того, что умеет или чего не умеет конкретный браузер пользователя.
Почему мы выбрали Google Speech API
В интернете есть много сервисов, которые дают приложениям возможность распознавать речь через API. Мы остановились на Google Speech API: он даёт качественный сервис потокового распознавания, действительно эффективный, а ответ в режиме реального времени для нас критически важен.
SDK-библиотека предоставляет и службу асинхронного распознавания (через gRPC и REST API), и службу распознавания в реальном времени — последнюю только через gRPC API.
Наша цель — распознавание в режиме реального времени, поэтому мы выбрали именно потоковое распознавание. Google фактически предоставляет этот API только через вызов gRPC, используя потоковую функцию протокола gRPC.
Архитектура: браузер, сервер, служба распознавания
Именно поэтому передавать аудио напрямую из браузера через gRPC невозможно. Браузер действительно умеет делать gRPC-вызовы, но потоково — никак.
Поэтому мы выбрали другой путь: передавать данные потоком на сервер, а уже оттуда — в потоковое API распознавания Google, и обратно, чтобы забрать результат распознавания.
Вариантов потоковой передачи аудиоданных из браузера на сервер немного. Фактически единственные системы, способные отдавать данные из браузера потоком, — это протокол webRTC и WebSocket. Для простоты мы выбрали WebSocket. Серверную часть имеет смысл разрабатывать на nodeJS: там простая обработка потоков и очень несложная реализация веб-сокетов.

- Подключение. После нажатия кнопки записи подключаемся к WebSocket — мы хотим избежать длительного соединения, которое может замедлить работу сервера, — и добавляем слушателя для данных аудиопотока.
- Прослушивание. Слушаем события на микрофоне браузера.
- Отправка. Отправляем буфер данных на бэкенд через WebSocket.
- Передача дальше. Передаём данные с бэкенда в службу распознавания речи Google.
- Получение. Забираем результаты распознавания.
- Возврат во фронтенд. Отправляем результаты во фронтенд через веб-сокет, чтобы визуализировать их по мере поступления.
Реализация веб-сокета в браузере
Здесь важно помнить, что поток аудиоданных — это массив Float32Array, и его нужно преобразовать в буфер, а потом снова развернуть на сервере.
Массив Float32Array конвертируем в массив Int16Array и уже тогда отправляем на сервер. Без этого преобразования сервер не получит данные в нужном формате. Переменная ConversionFactor нужна исключительно для целей конвертации.
Реализация веб-сокета на сервере
Реализация на сервере стандартная, болезненных точек всего две:
- когда именно запускать распознавание через Google;
- как справиться с разницей между форматом данных потока, который приходит из веб-сокета, и форматом аудиопотока для потока распознавания речи.
Сначала нужно создать поток распознавания речи — детали есть в документации сервиса.
Дальше можно транслировать аудиоблоки, поступающие из браузера, в речевое API. И вот здесь всплывает вторая болезненная точка — форматы данных.
| Этап | Формат данных | Что делаем |
|---|---|---|
| Микрофон в браузере | Float32Array, стерео | Берём только один канал |
| Перед отправкой в веб-сокет | Int16Array | Конвертируем из Float32Array |
| На сервере из веб-сокета | Стандартный буфер, целые по 8 бит | Принимаем как есть |
| Перед записью в поток распознавания | Int16Array, кодировка «LINEAR16» | Конвертируем буфер |
Буфер, который приходит из веб-сокета, — это стандартный буфер, массив целых чисел по 8 бит каждое. А в настройках распознавания речи мы указали, что аудиокодировка — «LINEAR16», то есть массив целых чисел по 16 бит каждое. Поэтому стандартный буфер нужно преобразовать в Int16Array, прежде чем записывать его в поток распознавания.
Поток распознавания, который возвращает библиотека распознавания речи Google, — это обычный стандартный поток, и обрабатывать его можно привычным способом.
Результат
В итоге мы получаем кроссбраузерную систему распознавания речи, которая упрощает взаимодействие пользователя с сайтом. Покупателю больше не нужно набирать название товара руками — достаточно сказать его вслух.
Если планируете что-то подобное в своём проекте, начинайте с двух проверок: поддержка getUserMedia / Stream API в нужных вам браузерах и возможности выбранного сервиса speech-to-text. Именно эти две вещи определяют, насколько сложной получится остальная работа.
