const messageHistory = document.getElementById('chatHistory') let stream; let ws let silenceDetectorNode; let silenceCount = 0 let recording = false; let mediaRecorder; let continuousRecorder let audioContext; let source; let currentAudioChunks = []; let allAudioChunks = []; const uuid = generateUUID() const startRecordingButton = document.getElementById('startRecording') const loadingModal = document.getElementById('loadingModal'); function makeLoading() { loadingModal.style.visibility = 'visible'; } function stopLoading() { loadingModal.style.visibility = 'hidden'; } function showMessage(message_text) { const message = document.getElementById('message'); message.innerText = message_text message.style.display = 'block'; setTimeout(function () { message.style.display = 'none'; }, 2000); } function createMessage(type, message) { const newMessage = document.createElement('div') newMessage.className = 'message rounded-4 bg-white mb-4 mx-4 py-2 px-3 border' newMessage.innerHTML = `
${type}
${message} ` messageHistory.appendChild(newMessage) } function playResponse(data) { const response = JSON.parse(data) createMessage('User', response['user_query']) stopLoading() console.log(response) const audioSrc = `data:audio/mp3;base64,${response['voice_response']}`; const audio = new Audio(audioSrc) audio.play() audio.onended = () => { recording = true createMessage('Liza', response['ai_response']) showMessage('You can speak!') startMediaRecorder() } } startRecordingButton.addEventListener('click', async () => { if (!recording) { if (mediaRecorder && mediaRecorder.state !== 'inactive') { mediaRecorder.stop(); } stream = await navigator.mediaDevices.getUserMedia({audio: true, video: false}); mediaRecorder = new MediaRecorder(stream); ws = new WebSocket(`wss://brestok-psycological-bot.hf.space/ws/${uuid}`); ws.onclose = (event) => { if (mediaRecorder && mediaRecorder.state !== 'inactive') { mediaRecorder.stop(); } } ws.onerror = (error) => { alert('Something was wrong. Try again later.') console.log(error) window.location.reload() }; ws.onmessage = (event) => { const response = event.data playResponse(response) } startRecordingButton.innerHTML = 'Stop call'; try { audioContext = new AudioContext(); await audioContext.audioWorklet.addModule('../../../static/js/audio-processor.js'); silenceDetectorNode = new AudioWorkletNode(audioContext, 'silence-detector-processor'); silenceDetectorNode.port.onmessage = (event) => { if (event.data.type === 'silence') { if (currentAudioChunks.length > 0) { if (silenceCount === 0) { silenceCount += 1; stopRecorder(); } } } else if (event.data.type === 'sound') { silenceCount = 0; } }; source = audioContext.createMediaStreamSource(stream); mediaRecorder.start(1000); mediaRecorder.ondataavailable = event => { currentAudioChunks.push(event.data); }; source.connect(silenceDetectorNode).connect(audioContext.destination); continuousRecorder = new MediaRecorder(stream); continuousRecorder.start(); continuousRecorder.ondataavailable = event => { allAudioChunks.push(event.data); }; recording = true; } catch (error) { console.error('Access to microphone denied:', error); } } else { await stopRecording(); } }); async function stopRecording() { // startRecordingButton.innerHTML = 'Start recording'; // recording = false; // mediaRecorder.stop(); // continuousRecorder.stop(); // silenceDetectorNode.disconnect(); // source.disconnect(); // audioContext.close(); // currentAudioChunks = []; window.location.reload() } function sendAudioToServer(audioBlob) { return new Promise((resolve, reject) => { console.log("Sending audio to server...", audioBlob); const reader = new FileReader(); reader.readAsDataURL(audioBlob); reader.onloadend = () => { let base64String = reader.result; base64String = base64String.split(',')[1]; const dataWS = {'audio': base64String}; ws.send(JSON.stringify(dataWS)); makeLoading() resolve(); }; reader.onerror = reject; }); } async function stopRecorder() { if (mediaRecorder && mediaRecorder.state !== 'inactive') { mediaRecorder.stop(); } await sendAudioToServer(new Blob(currentAudioChunks, {type: 'audio/wav'})); currentAudioChunks = []; } async function startMediaRecorder() { mediaRecorder = new MediaRecorder(stream); mediaRecorder.start(1000); mediaRecorder.ondataavailable = event => { currentAudioChunks.push(event.data); } } function generateUUID() { const arr = new Uint8Array(16) window.crypto.getRandomValues(arr) arr[6] = (arr[6] & 0x0f) | 0x40 arr[8] = (arr[8] & 0x3f) | 0x80 return ([...arr].map((b, i) => (i === 4 || i === 6 || i === 8 || i === 10 ? "-" : "") + b.toString(16).padStart(2, "0") ).join("")) }