/* ============================================================ SoundCloud-Style Audio Recorder + Waveform Display + Analyzer ============================================================ */ // Global variables let audioContext; let gumStream; let rec; let input; // DOM Elements const recordBtn = document.getElementById("recordButton"); const stopBtn = document.getElementById("stopButton"); const pauseBtn = document.getElementById("pauseButton"); const recordingsList = document.getElementById("recordingsList"); const uploadButton = document.getElementById("upload-button"); const uploadInput = document.getElementById("audio-file"); const responseBox = document.getElementById("response"); const metadataBox = document.getElementById("metadata-display"); // ============================== // Recording Controls // ============================== recordBtn.addEventListener("click", startRecording); stopBtn.addEventListener("click", stopRecording); pauseBtn.addEventListener("click", pauseRecording); // Start recording function startRecording() { console.log("Recording started..."); recordBtn.disabled = true; stopBtn.disabled = false; pauseBtn.disabled = false; navigator.mediaDevices.getUserMedia({ audio: true }).then(function (stream) { audioContext = new (window.AudioContext || window.webkitAudioContext)(); document.getElementById("formats").innerText = "Sample rate: " + audioContext.sampleRate + " Hz"; gumStream = stream; input = audioContext.createMediaStreamSource(stream); rec = new Recorder(input, { numChannels: 1 }); rec.record(); }).catch(function (e) { console.error("Could not start recording:", e); recordBtn.disabled = false; stopBtn.disabled = true; pauseBtn.disabled = true; }); } // Pause recording function pauseRecording() { if (rec.recording) { rec.stop(); pauseBtn.innerText = "Resume"; } else { rec.record(); pauseBtn.innerText = "Pause"; } } // Stop recording function stopRecording() { console.log("Recording stopped."); stopBtn.disabled = true; recordBtn.disabled = false; pauseBtn.disabled = true; pauseBtn.innerText = "Pause"; rec.stop(); gumStream.getAudioTracks()[0].stop(); rec.exportWAV(createWaveformItem); } // ============================== // File Upload // ============================== uploadButton.addEventListener("click", () => { if (!uploadInput.files.length) { alert("Select at least one file."); return; } Array.from(uploadInput.files).forEach((file) => handleUploadedFile(file)); }); function handleUploadedFile(file) { const url = URL.createObjectURL(file); createWaveformItem(file, url); } // ============================== // Create SoundCloud-like Waveform // ============================== function createWaveformItem(blob, forcedUrl = null) { const url = forcedUrl || URL.createObjectURL(blob); const li = document.createElement("li"); li.className = "wave-item"; const container = document.createElement("div"); container.className = "wave-canvas"; const meta = document.createElement("div"); meta.className = "wave-meta"; meta.innerHTML = `
${blob.name || "recording.wav"}
`; // Play button const playBtn = document.createElement("button"); playBtn.className = "btn btn-play btn-sm"; playBtn.innerText = "Play"; // Analyze button const analyzeBtn = document.createElement("button"); analyzeBtn.className = "btn btn-outline-secondary btn-sm"; analyzeBtn.innerText = "Analyze"; const controls = document.createElement("div"); controls.className = "wave-controls"; controls.appendChild(analyzeBtn); controls.appendChild(playBtn); li.appendChild(controls); li.appendChild(container); li.appendChild(meta); recordingsList.appendChild(li); // Create WaveSurfer const wavesurfer = WaveSurfer.create({ container, autoCenter: true, waveColor: "#bbb", progressColor: "#ff5500", cursorColor: "#333", barWidth: 2, height: 70, responsive: true, }); // Analyze analyzeBtn.addEventListener("click", () => { analyzeAudio(blob); }); // Toggle play/pause playBtn.addEventListener("click", () => { wavesurfer.playPause(); playBtn.innerText = wavesurfer.isPlaying() ? "Pause" : "Play"; }); wavesurfer.load(url); // Metadata blob.arrayBuffer().then((buffer) => { const ctx = new AudioContext(); ctx.decodeAudioData(buffer).then((decoded) => { metadataBox.innerHTML = ` Channels: ${decoded.numberOfChannels}
Duration: ${decoded.duration.toFixed(2)}s
Sample Rate: ${decoded.sampleRate} Hz `; }); }); } // ============================== // Audio Analysis API (placeholder) // ============================== async function analyzeAudio(blob) { // Check if the response element exists const responseBox = document.getElementById('response'); if (!responseBox) return; responseBox.innerHTML = "Analyzing..."; // Convert Blob to File const file = new File([blob], blob.name || "recording.wav", { type: blob.type }); const formData = new FormData(); formData.append("files", file); try { // Replace the URL with your real backend endpoint const res = await fetch("/predict/", { method: "POST", body: formData, }); const data = await res.json(); let message = "Prediction Failed!"; if (data[0]?.status === "success") { const item = data[0]; const confidencePercent = Math.round(item.confidence * 100); message = `${item.filename} is ${item.label} with confidence of ${confidencePercent}%`; } let messageVolume = "... "; if (data[0]?.volume === "silent") { messageVolume = "The audio appears to be silent or contains no detectable speech. Please record/select another file! "; message = "" } if (data[0]?.volume === "augmented") { messageVolume = "The detected speech volume was low, so a volume augmentation was applied before analysis. "; } const volumeColor = "orange"; let predictionColor = message.includes("Genuine") ? "green" : "red"; responseBox.innerHTML = `
${messageVolume}
${message}
`; } catch (err) { responseBox.innerHTML = `
/!\ Analysis failed.
`; console.error(err); } }