let audioContext; let analyser; let sourceNode; let mediaRecorder; let audioChunks = []; document.addEventListener("DOMContentLoaded", () => { const audioResponseElement = document.getElementById("audioResponse"); const micButton = document.getElementById("micButton"); const blobs = document.querySelectorAll(".blob"); initAudioContext(audioResponseElement); function initAudioContext(audioElement) { audioContext = new (window.AudioContext || window.webkitAudioContext)(); analyser = audioContext.createAnalyser(); analyser.fftSize = 64; if (audioElement && !sourceNode) { sourceNode = audioContext.createMediaElementSource(audioElement); sourceNode.connect(analyser); analyser.connect(audioContext.destination); } } function startWaitAnimation() { document.getElementById("thought-bubble").style.display = "block"; micButton.classList.add("disabled"); micButton.disabled = true; } function stopWaitAnimation() { document.getElementById("thought-bubble").style.display = "none"; micButton.classList.remove("disabled"); micButton.disabled = false; } function enableMicButton() { micButton.classList.remove("disabled"); micButton.disabled = false; } function hideBlobs() { document.querySelector(".blob-container").style.display = "none"; } function showBlobs() { document.querySelector(".blob-container").style.display = "flex"; } async function startRecording() { audioChunks = []; try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); mediaRecorder = new MediaRecorder(stream); mediaRecorder.ondataavailable = (event) => audioChunks.push(event.data); mediaRecorder.start(); } catch (error) { console.error("Error accessing audio devices:", error); micButton.checked = false; // Uncheck the button in case of an error stopWaitAnimation(); } } function stopRecording() { mediaRecorder.stop(); mediaRecorder.onstop = async () => { const audioBlob = new Blob(audioChunks, { type: "audio/wav" }); const audioBase64 = await blobToBase64(audioBlob); sendAudioToServer(audioBase64); }; } function blobToBase64(blob) { const reader = new FileReader(); return new Promise((resolve) => { reader.onloadend = () => resolve(reader.result.split(",")[1]); reader.readAsDataURL(blob); }); } function sendAudioToServer(audioBase64) { fetch( "https://h8v918qrvg.execute-api.eu-central-1.amazonaws.com/Prod/sts/question", { method: "POST", headers: { "Content-Type": "text/plain" }, body: audioBase64, } ) .then((response) => response.text()) .then((data) => { if (data.message === "Question not provided in POST body") { console.error("Question not provided in POST body"); stopWaitAnimation(); enableMicButton(); // Re-enable the microphone button return; // Stop further processing } const audioSrc = `data:audio/wav;base64,${data}`; audioResponseElement.src = audioSrc; audioResponseElement.play().then(() => { visualize(); // Start the visualizer after the audio starts playing }); }) .catch((error) => { console.error("Error:", error); stopWaitAnimation(); }); } micButton.addEventListener("change", () => { hideBlobs(); if (micButton.checked) { startRecording(); } else { stopRecording(); startWaitAnimation(); } }); function visualize() { if (!audioContext) { console.error("AudioContext not initialized"); return; } if (!sourceNode) { console.error("SourceNode not initialized"); return; } const bufferLength = analyser.frequencyBinCount; const dataArray = new Uint8Array(bufferLength); function draw() { requestAnimationFrame(draw); analyser.getByteFrequencyData(dataArray); const segmentLength = Math.floor(bufferLength / blobs.length); for (let i = 0; i < blobs.length; i++) { // Use an average value of a segment of the dataArray for each blob const dataValue = dataArray[(i * segmentLength) / 2] || 0; // Fallback to 0 if undefined const height = (dataValue / 128.0) * 50 + 50; blobs[i].style.height = `${height}px`; } } draw(); } audioResponseElement.onended = () => { hideBlobs(); // Hide the blobs once the audio has finished playing stopWaitAnimation(); enableMicButton(); // Ensure thought bubble is not showing }; audioResponseElement.onplay = () => { showBlobs(); // Show the blobs when audio starts playing stopWaitAnimation(); // Hide the thought bubble when audio starts playing }; document.body.addEventListener("click", () => { if (audioContext && audioContext.state === "suspended") { audioContext.resume(); } }); });