lct-09: звук звонящего на фронте, перебивание, аудио-фон
Очередь AudioBufferSourceNode, а не <audio>: перебивание требует оборвать звук мгновенно и выбросить очередь. Планирование встык по currentTime с упреждением 50 мс — иначе между чанками щелчок. Перебивание двухслойное: фронт гасит звук по громкости микрофона за два кадра (40 мс), сервер подтверждает по VAD за 88–90 мс и присылает tts.cancel. Сервер — авторитет, фронт — скорость. Логика вынесена из WebAudio и проверена в Node: очередь планируется без щелей и наложений, сброс останавливает все чанки, гейт не срабатывает на шуме и ловит начало речи ровно один раз. Нет файла фона — звонок идёт без фона с предупреждением в консоли, а не падает: записи это контент, их кладёт методист. Путь через браузер с живым микрофоном не проверен: шаги записаны в карточке.
This commit is contained in:
parent
456f3cd34d
commit
750ded2991
9 changed files with 402 additions and 98 deletions
|
|
@ -15,6 +15,8 @@ export type ChannelStatus = "connecting" | "open" | "reconnecting" | "closed";
|
|||
|
||||
interface ChannelOptions<In> {
|
||||
onEvent?: (event: In) => void;
|
||||
/** Бинарный кадр: звук звонящего, без обёртки JSON. */
|
||||
onBinary?: (frame: ArrayBuffer) => void;
|
||||
onStatus?: (status: ChannelStatus) => void;
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +51,10 @@ export class Channel<In extends { type: string }, Out extends { type: string }>
|
|||
this.options.onStatus?.("open");
|
||||
};
|
||||
socket.onmessage = (message) => {
|
||||
if (typeof message.data !== "string") return; // бинарь курсанту разбирает аудиослой (lct-09)
|
||||
if (typeof message.data !== "string") {
|
||||
this.options.onBinary?.(message.data as ArrayBuffer);
|
||||
return;
|
||||
}
|
||||
this.options.onEvent?.(JSON.parse(message.data) as In);
|
||||
};
|
||||
socket.onclose = () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue