lorvjoaie / index.html
puzan789's picture
Update index.html
bc2bce0 verified
<!DOCTYPE html>
<html>
<head>
<title>Voice Agent</title>
<style>
.call-button {
padding: 20px 40px;
font-size: 24px;
border-radius: 50px;
background-color: #4CAF50;
color: white;
border: none;
cursor: pointer;
transition: all 0.3s;
}
.call-button.active {
background-color: #f44336;
}
.status {
margin-top: 20px;
font-size: 18px;
}
.volume-meter {
width: 300px;
height: 20px;
background-color: #ddd;
margin: 20px auto;
border-radius: 10px;
overflow: hidden;
}
.volume-level {
height: 100%;
width: 0%;
background-color: #4CAF50;
transition: width 0.1s;
}
</style>
</head>
<body>
<div style="text-align: center; padding: 50px;">
<button id="callButton" class="call-button">Start Call</button>
<div class="volume-meter">
<div id="volumeLevel" class="volume-level"></div>
</div>
<div id="status" class="status"></div>
</div>
<script>
let ws, mediaRecorder, audioChunks = [];
let isListening = false;
let isPlayingResponse = false;
let audioContext, analyser, dataArray;
const silenceThreshold = -40;
const silenceTime = 2.0;
let lastAudioLevel = Date.now();
const callButton = document.getElementById('callButton');
const statusDiv = document.getElementById('status');
const volumeLevel = document.getElementById('volumeLevel');
async function startCall() {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
setupAudioAnalyser(stream);
ws = new WebSocket(`wss://puzan789-arkos.hf.space/ws/voicechat`);
ws.onopen = () => {
statusDiv.textContent = 'Connected - Ready to listen';
callButton.classList.add('active');
callButton.textContent = 'End Call';
startListening(stream);
};
ws.onmessage = async (event) => {
const data = JSON.parse(event.data);
stopListening(); // Stop listening while processing response
if (data.audio) {
statusDiv.innerHTML = `<b>You said:</b> ${data.transcript}<br><b>AI is responding...</b>`;
// Play audio response
const binaryString = atob(data.audio);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
const audioBlob = new Blob([bytes], { type: 'audio/wav' });
const audioUrl = URL.createObjectURL(audioBlob);
const audio = new Audio(audioUrl);
audio.onended = () => {
ws.send("audio_complete");
statusDiv.innerHTML = `<b>You said:</b> ${data.transcript}<br><b>AI responded:</b> ${data.response}<br><b>Ready for next input</b>`;
startListening(stream); // Resume listening
};
audio.play();
}
};
ws.onclose = () => stopCall();
} catch (error) {
console.error('Error:', error);
statusDiv.textContent = 'Error: ' + error.message;
}
}
function setupAudioAnalyser(stream) {
audioContext = new AudioContext();
analyser = audioContext.createAnalyser();
const source = audioContext.createMediaStreamSource(stream);
source.connect(analyser);
analyser.fftSize = 2048;
dataArray = new Float32Array(analyser.frequencyBinCount);
}
function getAudioLevel() {
analyser.getFloatTimeDomainData(dataArray);
let sum = 0;
for (let i = 0; i < dataArray.length; i++) {
sum += dataArray[i] * dataArray[i];
}
const rms = Math.sqrt(sum / dataArray.length);
const db = 20 * Math.log10(rms);
return db;
}
function updateVolumeMeter() {
if (analyser && isListening) {
const db = getAudioLevel();
const normalizedDb = Math.max(0, Math.min(100, (db + 60) * 2));
volumeLevel.style.width = normalizedDb + '%';
if (db > silenceThreshold) {
lastAudioLevel = Date.now();
} else if (Date.now() - lastAudioLevel > silenceTime * 1000) {
sendAudio();
lastAudioLevel = Date.now();
}
}
requestAnimationFrame(updateVolumeMeter);
}
function startListening(stream) {
isListening = true;
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.ondataavailable = async (event) => {
if (event.data.size > 0) {
audioChunks.push(event.data);
}
};
mediaRecorder.start(250);
updateVolumeMeter();
}
function stopListening() {
isListening = false;
if (mediaRecorder && mediaRecorder.state === 'recording') {
mediaRecorder.stop();
}
audioChunks = [];
volumeLevel.style.width = '0%';
}
async function sendAudio() {
if (audioChunks.length > 0 && ws.readyState === WebSocket.OPEN) {
const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
ws.send(await audioBlob.arrayBuffer());
audioChunks = [];
stopListening();
}
}
function stopCall() {
if (ws) ws.close();
stopListening();
if (audioContext) audioContext.close();
callButton.classList.remove('active');
callButton.textContent = 'Start Call';
}
callButton.addEventListener('click', () => {
if (!ws || ws.readyState === WebSocket.CLOSED) {
startCall();
} else {
stopCall();
}
});
</script>
</body>
</html>