Потоковое распознавание (WebSocket)
Потоковый режим позволяет отправлять аудио в реальном времени и получать результаты по мере распознавания — например, для live-транскрибации звонков или совещаний.
WS /api/v1/speech-to-text/live?lang=ru
Query-параметры:
| Параметр | Тип | Описание |
|---|---|---|
lang | string | Язык распознавания, например ru. По умолчанию ru. |
Протокол
Подключение: клиент устанавливает WebSocket-соединение с заголовком Authorization: Bearer <api_key>. Сервер подтверждает подключение (101 Switching Protocols).
Клиент → сервер (бинарные фреймы):
| Параметр | Значение |
|---|---|
| Формат | float32 little-endian (PCM) |
| Частота дискретизации | 16 000 Гц |
| Каналы | 1 (mono) |
| Размер чанка | 4096–32768 байт (рекомендуется) |
| Частота отправки | Каждые 100–500 мс |
Сервер → клиент (текстовые фреймы): JSON-объект с данными распознанного сегмента.
| Поле | Тип | Описание |
|---|---|---|
start | float | Время начала сегмента (секунды) |
end | float? | Время конца сегмента |
text | string | Распознанный текст |
speech_prob | float? | Вероятность наличия речи |
vad_prob | float? | Вероятность VAD |
volume_70 | float? | Уровень громкости (70-й перцентиль) |
words | array? | Детализация по словам |
channel | string? | Идентификатор канала |
speaker | string? | Идентификатор спикера |
emotion | object? | Эмоции: {positive, angry, sad, neutral} (значения 0–1) |
Завершение: клиент закрывает WebSocket (close frame). Сервер дожидается обработки оставшихся данных, отправляет финальные результаты и закрывает соединение.
Ошибки: сервер отправляет текстовый фрейм Error: описание ошибки.
JavaScript
const ws = new WebSocket("wss://cognico.ru/api/v1/speech-to-text/live", [], {
headers: { "Authorization": "Bearer YOUR_API_KEY" },
});
ws.onopen = () => {
console.log("Соединение установлено");
};
ws.onmessage = (event) => {
const segment = JSON.parse(event.data);
console.log(`[${segment.start.toFixed(1)}s] ${segment.text}`);
};
ws.onerror = (error) => {
console.error("Ошибка WebSocket:", error);
};
// Отправка аудиочанка (ArrayBuffer)
ws.send(audioChunk);
// Завершение сессии
ws.close();
Пример с захватом микрофона в браузере (AudioWorklet, float32 PCM 16 kHz):
const stream = await navigator.mediaDevices.getUserMedia({
audio: { sampleRate: 16000, channelCount: 1 },
});
const audioCtx = new AudioContext({ sampleRate: 16000 });
const source = audioCtx.createMediaStreamSource(stream);
// Регистрируем процессор для получения сырых float32 сэмплов
await audioCtx.audioWorklet.addModule(
URL.createObjectURL(new Blob([`
class Sender extends AudioWorkletProcessor {
process(inputs) {
if (inputs[0][0]?.length) this.port.postMessage(inputs[0][0]);
return true;
}
}
registerProcessor("sender", Sender);
`], { type: "application/javascript" }))
);
const node = new AudioWorkletNode(audioCtx, "sender");
source.connect(node).connect(audioCtx.destination);
const ws = new WebSocket("wss://cognico.ru/api/v1/speech-to-text/live");
node.port.onmessage = ({ data }) => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(new Float32Array(data).buffer);
}
};
ws.onmessage = (event) => {
const segment = JSON.parse(event.data);
document.getElementById("transcript").textContent += segment.text + " ";
};
Python
import asyncio
import json
import struct
import wave
import websockets
def wav_to_f32_chunks(path: str, chunk_samples: int = 4096):
"""Читает WAV-файл и отдаёт чанки в формате float32 PCM."""
with wave.open(path, "rb") as wf:
assert wf.getnchannels() == 1, "Ожидается mono"
assert wf.getsampwidth() == 2, "Ожидается 16-bit"
assert wf.getframerate() == 16000, "Ожидается 16 kHz"
while data := wf.readframes(chunk_samples):
# int16 → float32
samples = struct.unpack(f"<{len(data)//2}h", data)
yield struct.pack(f"<{len(samples)}f", *(s / 32768.0 for s in samples))
async def recognize_stream(audio_file_path: str, api_key: str):
uri = "wss://cognico.ru/api/v1/speech-to-text/live"
headers = {"Authorization": f"Bearer {api_key}"}
async with websockets.connect(uri, extra_headers=headers) as ws:
async def send_audio():
for chunk in wav_to_f32_chunks(audio_file_path):
await ws.send(chunk)
await asyncio.sleep(0.1) # имитация реального времени
await ws.close()
async def receive_results():
async for message in ws:
segment = json.loads(message)
print(f"[{segment['start']:.1f}s] {segment['text']}")
await asyncio.gather(send_audio(), receive_results())
asyncio.run(recognize_stream("recording.wav", "YOUR_API_KEY"))