()
| 253 | } |
| 254 | |
| 255 | async function connect() { |
| 256 | const model = getModel(); |
| 257 | if (!model) { |
| 258 | alert('Please select a pipeline model first.'); |
| 259 | return; |
| 260 | } |
| 261 | |
| 262 | if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { |
| 263 | setStatus('error', 'Microphone access requires HTTPS or localhost.'); |
| 264 | return; |
| 265 | } |
| 266 | |
| 267 | setStatus('connecting', 'Connecting...'); |
| 268 | connectButton.style.display = 'none'; |
| 269 | disconnectButton.style.display = ''; |
| 270 | testToneButton.style.display = ''; |
| 271 | diagnosticsButton.style.display = ''; |
| 272 | |
| 273 | try { |
| 274 | // Get microphone access |
| 275 | localStream = await navigator.mediaDevices.getUserMedia({ audio: true }); |
| 276 | |
| 277 | // Create peer connection |
| 278 | pc = new RTCPeerConnection({}); |
| 279 | |
| 280 | // Add local audio track |
| 281 | for (const track of localStream.getAudioTracks()) { |
| 282 | pc.addTrack(track, localStream); |
| 283 | } |
| 284 | |
| 285 | // Handle remote audio track (server's TTS output) |
| 286 | pc.ontrack = (event) => { |
| 287 | audioPlayback.srcObject = event.streams[0]; |
| 288 | // If diagnostics panel is open, start analyzing the new stream |
| 289 | if (diagVisible) startDiagnostics(); |
| 290 | }; |
| 291 | |
| 292 | // Create the events data channel (client must create it so m=application |
| 293 | // is included in the SDP offer — the answerer cannot add new m-lines) |
| 294 | dc = pc.createDataChannel('oai-events'); |
| 295 | dc.onmessage = (msg) => { |
| 296 | try { |
| 297 | const text = typeof msg.data === 'string' |
| 298 | ? msg.data |
| 299 | : new TextDecoder().decode(msg.data); |
| 300 | const event = JSON.parse(text); |
| 301 | handleServerEvent(event); |
| 302 | } catch (e) { |
| 303 | console.error('Failed to parse server event:', e); |
| 304 | } |
| 305 | }; |
| 306 | dc.onclose = () => { |
| 307 | console.log('Data channel closed'); |
| 308 | }; |
| 309 | |
| 310 | pc.onconnectionstatechange = () => { |
| 311 | console.log('Connection state:', pc.connectionState); |
| 312 | if (pc.connectionState === 'connected') { |
no test coverage detected