Текстовый файл из учебного комплекта, не свидетельство установленного runtime. Вернуться к учебнику · Исходный файл
1 import type { AppConfig } from "../config/appConfig"; 2 import type { AssistantElements } from "../ui/elements"; 3 import type { AudioState } from "../audio/audioState"; 4 import { startMic, stopMic } from "../audio/mic"; 5 import { negotiate } from "./negotiate"; 6 import { createEnergyVad, type VadInstance } from "../audio/vad"; 7 import { startWaveforms } from "../ui/waves"; 8 9 type Session = { 10 peer: RTCPeerConnection; 11 sender: RTCRtpSender; 12 micStream: MediaStream; 13 vad: VadInstance | null; 14 wavesStop: (() => void) | null; 15 }; 16 17 let session: Session | null = null; 18 19 export async function connectSession( 20 config: AppConfig, 21 el: AssistantElements, 22 audio: AudioState, 23 onVadText: (t: string) => void, 24 onSpeechActivityChange?: (active: boolean) => void, 25 opts?: { sessionId?: string | null }, 26 ): Promise<string | null> { 27 if (session) return null; 28 29 const micStream = await startMic(audio); 30 let peer: RTCPeerConnection | null = null; 31 let vad: VadInstance | null = null; 32 let wavesStop: (() => void) | null = null; 33 34 try { 35 peer = new RTCPeerConnection({ 36 iceServers: config.webrtc.iceServers, 37 iceCandidatePoolSize: config.webrtc.iceCandidatePoolSize, 38 bundlePolicy: config.webrtc.bundlePolicy, 39 rtcpMuxPolicy: config.webrtc.rtcpMuxPolicy, 40 }); 41 42 peer.ontrack = (e) => { 43 if (el.remoteAudio) el.remoteAudio.srcObject = e.streams[0]; 44 }; 45 46 const track = micStream.getAudioTracks()[0]; 47 const sender = peer.addTrack(track, micStream); 48 49 if (audio.vadEnabled) { 50 vad = createEnergyVad( 51 track, 52 config, 53 () => audio.vadThreshold, 54 (v) => onVadText(`${v} dBFS`), 55 onSpeechActivityChange, 56 ); 57 await sender.replaceTrack(vad.track); 58 } 59 60 const waves = startWaveforms(el, config, micStream); 61 wavesStop = waves.stop; 62 63 const sessionId = await negotiate(peer, config, opts); 64 65 session = { peer, sender, micStream, vad, wavesStop }; 66 return sessionId; 67 } catch (error) { 68 try { 69 vad?.stop(); 70 } catch { 71 // Preserve the connection error while attempting every cleanup step. 72 } 73 try { 74 wavesStop?.(); 75 } catch { 76 // Preserve the connection error while attempting every cleanup step. 77 } 78 try { 79 stopMic(micStream); 80 } catch { 81 // Preserve the connection error while attempting every cleanup step. 82 } 83 try { 84 peer?.close(); 85 } catch { 86 // Preserve the connection error while attempting every cleanup step. 87 } 88 throw error; 89 } 90 } 91 92 export function disconnectSession(): void { 93 if (!session) return; 94 95 session.vad?.stop(); 96 session.wavesStop?.(); 97 98 stopMic(session.micStream); 99 session.peer.close(); 100 101 session = null; 102 } 103 104 export function isConnected(): boolean { 105 return !!session; 106 } 107 108 export function setMicEnabled(enabled: boolean): void { 109 if (!session) return; 110 const track = session.sender.track; 111 if (track) track.enabled = enabled; 112 }