Spaces:
Running
Running
Update index.html
Browse files- index.html +52 -6
index.html
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
-
<title>Voice Chat Bot with Echo Cancellation</title>
|
7 |
<script src="https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.js"></script>
|
8 |
<script src="https://cdn.jsdelivr.net/npm/@ricky0123/[email protected]/dist/bundle.min.js"></script>
|
9 |
<script src="https://cdn.jsdelivr.net/npm/@xenova/[email protected]"></script>
|
@@ -136,7 +136,7 @@
|
|
136 |
font-size: 14px;
|
137 |
padding: 5px 10px;
|
138 |
}
|
139 |
-
#localVideo {
|
140 |
display: none;
|
141 |
}
|
142 |
</style>
|
@@ -169,7 +169,8 @@
|
|
169 |
<div id="logs"></div>
|
170 |
<button id="clear-logs">Clear</button>
|
171 |
</div>
|
172 |
-
<video id="localVideo" autoplay
|
|
|
173 |
|
174 |
<script type="module">
|
175 |
import { pipeline, env } from 'https://cdn.jsdelivr.net/npm/@xenova/[email protected]';
|
@@ -183,6 +184,7 @@
|
|
183 |
const logsDiv = document.getElementById('logs');
|
184 |
const clearLogsButton = document.getElementById('clear-logs');
|
185 |
const localVideo = document.getElementById('localVideo');
|
|
|
186 |
|
187 |
let myvad;
|
188 |
let sttPipeline;
|
@@ -196,6 +198,9 @@
|
|
196 |
let microphoneStream;
|
197 |
let isSpeaking = false;
|
198 |
let currentAudioSource = null;
|
|
|
|
|
|
|
199 |
|
200 |
function createVisualizer() {
|
201 |
const barCount = 64;
|
@@ -305,8 +310,13 @@
|
|
305 |
analyser.fftSize = 128;
|
306 |
dataArray = new Uint8Array(analyser.frequencyBinCount);
|
307 |
|
308 |
-
localVideo.muted = true;
|
309 |
localVideo.volume = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
310 |
|
311 |
// Request both audio and video streams
|
312 |
microphoneStream = await navigator.mediaDevices.getUserMedia({
|
@@ -320,7 +330,31 @@
|
|
320 |
console.log('Active constraints:', microphoneStream.getAudioTracks()[0].getConstraints());
|
321 |
console.log('Microphone stream settings:', microphoneStream.getAudioTracks()[0].getSettings());
|
322 |
|
323 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
324 |
source.connect(analyser);
|
325 |
|
326 |
myvad = await vad.MicVAD.new({
|
@@ -370,13 +404,25 @@
|
|
370 |
if (localVideo) {
|
371 |
localVideo.srcObject = null;
|
372 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
373 |
stopCurrentAudio();
|
374 |
startButton.textContent = 'Begin Call';
|
375 |
isListening = false;
|
376 |
addLog('System: Stopped listening.');
|
377 |
cancelAnimationFrame(animationId);
|
378 |
addLog('System: Microphone closed');
|
379 |
-
|
380 |
|
381 |
startButton.addEventListener('click', toggleListening);
|
382 |
clearLogsButton.addEventListener('click', () => {
|
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>Voice Chat Bot with Advanced Echo Cancellation</title>
|
7 |
<script src="https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.js"></script>
|
8 |
<script src="https://cdn.jsdelivr.net/npm/@ricky0123/[email protected]/dist/bundle.min.js"></script>
|
9 |
<script src="https://cdn.jsdelivr.net/npm/@xenova/[email protected]"></script>
|
|
|
136 |
font-size: 14px;
|
137 |
padding: 5px 10px;
|
138 |
}
|
139 |
+
#localVideo, #remoteVideo {
|
140 |
display: none;
|
141 |
}
|
142 |
</style>
|
|
|
169 |
<div id="logs"></div>
|
170 |
<button id="clear-logs">Clear</button>
|
171 |
</div>
|
172 |
+
<video id="localVideo" autoplay></video>
|
173 |
+
<video id="remoteVideo" autoplay></video>
|
174 |
|
175 |
<script type="module">
|
176 |
import { pipeline, env } from 'https://cdn.jsdelivr.net/npm/@xenova/[email protected]';
|
|
|
184 |
const logsDiv = document.getElementById('logs');
|
185 |
const clearLogsButton = document.getElementById('clear-logs');
|
186 |
const localVideo = document.getElementById('localVideo');
|
187 |
+
const remoteVideo = document.getElementById('remoteVideo');
|
188 |
|
189 |
let myvad;
|
190 |
let sttPipeline;
|
|
|
198 |
let microphoneStream;
|
199 |
let isSpeaking = false;
|
200 |
let currentAudioSource = null;
|
201 |
+
let rtcConnection = null;
|
202 |
+
let rtcLoopbackConnection = null;
|
203 |
+
let loopbackStream = new MediaStream();
|
204 |
|
205 |
function createVisualizer() {
|
206 |
const barCount = 64;
|
|
|
310 |
analyser.fftSize = 128;
|
311 |
dataArray = new Uint8Array(analyser.frequencyBinCount);
|
312 |
|
|
|
313 |
localVideo.volume = 0;
|
314 |
+
localVideo.muted = true;
|
315 |
+
document.getElementById('localVideo').volume = 0;
|
316 |
+
|
317 |
+
remoteVideo.volume = 0;
|
318 |
+
remoteVideo.muted = true;
|
319 |
+
document.getElementById('remoteVideo').volume = 0;
|
320 |
|
321 |
// Request both audio and video streams
|
322 |
microphoneStream = await navigator.mediaDevices.getUserMedia({
|
|
|
330 |
console.log('Active constraints:', microphoneStream.getAudioTracks()[0].getConstraints());
|
331 |
console.log('Microphone stream settings:', microphoneStream.getAudioTracks()[0].getSettings());
|
332 |
|
333 |
+
// Implement loopback hack for improved echo cancellation
|
334 |
+
const offerOptions = {
|
335 |
+
offerToReceiveAudio: true,
|
336 |
+
offerToReceiveVideo: false,
|
337 |
+
};
|
338 |
+
|
339 |
+
rtcConnection = new RTCPeerConnection();
|
340 |
+
rtcLoopbackConnection = new RTCPeerConnection();
|
341 |
+
|
342 |
+
rtcConnection.onicecandidate = e => e.candidate && rtcLoopbackConnection.addIceCandidate(new RTCIceCandidate(e.candidate));
|
343 |
+
rtcLoopbackConnection.onicecandidate = e => e.candidate && rtcConnection.addIceCandidate(new RTCIceCandidate(e.candidate));
|
344 |
+
|
345 |
+
rtcLoopbackConnection.ontrack = e => e.streams[0].getTracks().forEach(track => loopbackStream.addTrack(track));
|
346 |
+
|
347 |
+
microphoneStream.getTracks().forEach(track => rtcConnection.addTrack(track, microphoneStream));
|
348 |
+
|
349 |
+
const offer = await rtcConnection.createOffer(offerOptions);
|
350 |
+
await rtcConnection.setLocalDescription(offer);
|
351 |
+
await rtcLoopbackConnection.setRemoteDescription(offer);
|
352 |
+
const answer = await rtcLoopbackConnection.createAnswer();
|
353 |
+
await rtcLoopbackConnection.setLocalDescription(answer);
|
354 |
+
await rtcConnection.setRemoteDescription(answer);
|
355 |
+
|
356 |
+
// Use the loopback stream for audio processing
|
357 |
+
const source = audioContext.createMediaStreamSource(loopbackStream);
|
358 |
source.connect(analyser);
|
359 |
|
360 |
myvad = await vad.MicVAD.new({
|
|
|
404 |
if (localVideo) {
|
405 |
localVideo.srcObject = null;
|
406 |
}
|
407 |
+
if (remoteVideo) {
|
408 |
+
remoteVideo.srcObject = null;
|
409 |
+
}
|
410 |
+
if (rtcConnection) {
|
411 |
+
rtcConnection.close();
|
412 |
+
rtcConnection = null;
|
413 |
+
}
|
414 |
+
if (rtcLoopbackConnection) {
|
415 |
+
rtcLoopbackConnection.close();
|
416 |
+
rtcLoopbackConnection = null;
|
417 |
+
}
|
418 |
+
loopbackStream = new MediaStream();
|
419 |
stopCurrentAudio();
|
420 |
startButton.textContent = 'Begin Call';
|
421 |
isListening = false;
|
422 |
addLog('System: Stopped listening.');
|
423 |
cancelAnimationFrame(animationId);
|
424 |
addLog('System: Microphone closed');
|
425 |
+
}
|
426 |
|
427 |
startButton.addEventListener('click', toggleListening);
|
428 |
clearLogsButton.addEventListener('click', () => {
|