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/vad-web@0.0.18/dist/bundle.min.js"></script>
|
| 9 |
<script src="https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.2"></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/transformers@2.17.2';
|
|
@@ -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/vad-web@0.0.18/dist/bundle.min.js"></script>
|
| 9 |
<script src="https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.2"></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/transformers@2.17.2';
|
|
|
|
| 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', () => {
|