Завдання звучало просто: дати покупцеві змогу з будь-якого браузера й будь-якого пристрою продиктувати пошуковий запит словами. Голос перетворюється на текст у рядку пошуку, а окремі слова працюють як команди — «шукати», «скасувати» — і запускають відповідні дії на сайті. Нижче розбираємо, як влаштована така система зсередини: від мікрофона в браузері до відповіді служби розпізнавання й назад.
Завдання
Треба розробити функцію розпізнавання мовлення на вебсторінці інтернет-магазину. Покупець натискає кнопку запису, диктує запит — і бачить його текстом у пошуковому рядку. Частина сказаного обробляється як команди: «шукати» запускає пошук, «скасувати» скидає введене.
Ключова вимога — повна сумісність із найпоширенішими браузерами: 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. Саме ці дві речі визначають, наскільки складною вийде решта роботи.
