document.addEventListener('DOMContentLoaded', () => { console.log('DOM fully loaded and parsed'); const startRecordBtn = document.getElementById('startRecordBtn'); const stopRecordBtn = document.getElementById('stopRecordBtn'); const saveRecordBtn = document.getElementById('saveRecordBtn'); const waveformCanvas = document.getElementById('waveform'); const recordingsList = document.getElementById('recordingsList'); const canvasCtx = waveformCanvas.getContext('2d'); let mediaRecorder; let audioChunks = []; let audioContext; let analyser; let source; let animationFrameId; let streamReference; // Keep a reference to the stream async function loadRecordings() { console.log('Loading recordings...'); try { const response = await fetch('list_recordings.php'); const recordings = await response.json(); console.log('Recordings found:', recordings); recordingsList.innerHTML = ''; if (recordings.length > 0) { const list = document.createElement('div'); list.className = 'list-group'; recordings.forEach(recording => { const fileName = recording.split('/').pop(); const card = document.createElement('div'); card.className = 'list-group-item list-group-item-action d-flex justify-content-between align-items-center'; const nameSpan = document.createElement('span'); nameSpan.className = 'recording-name'; nameSpan.textContent = fileName; const playBtn = document.createElement('button'); playBtn.className = 'btn btn-play btn-sm rounded-pill'; playBtn.innerHTML = ''; playBtn.onclick = () => { const audio = new Audio(recording); audio.play(); }; card.appendChild(nameSpan); card.appendChild(playBtn); list.appendChild(card); }); recordingsList.appendChild(list); } else { recordingsList.innerHTML = '

No recordings yet.

Your saved recordings will appear here.
'; } } catch (error) { console.error('Failed to load recordings:', error); recordingsList.innerHTML = '

Could not load recordings.

'; } } startRecordBtn.addEventListener('click', async () => { console.log('Start recording button clicked'); try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); streamReference = stream; // Save stream reference console.log('Microphone access granted'); startRecordBtn.disabled = true; stopRecordBtn.disabled = false; saveRecordBtn.style.display = 'none'; console.log('UI updated for recording start'); audioContext = new (window.AudioContext || window.webkitAudioContext)(); analyser = audioContext.createAnalyser(); source = audioContext.createMediaStreamSource(stream); source.connect(analyser); analyser.fftSize = 2048; const bufferLength = analyser.frequencyBinCount; const dataArray = new Uint8Array(bufferLength); const draw = () => { animationFrameId = requestAnimationFrame(draw); analyser.getByteTimeDomainData(dataArray); canvasCtx.fillStyle = '#e9ecef'; // Match the light background canvasCtx.fillRect(0, 0, waveformCanvas.width, waveformCanvas.height); canvasCtx.lineWidth = 2; canvasCtx.strokeStyle = '#0d6efd'; // Primary button color canvasCtx.beginPath(); const sliceWidth = waveformCanvas.width * 1.0 / bufferLength; let x = 0; for (let i = 0; i < bufferLength; i++) { const v = dataArray[i] / 128.0; const y = v * waveformCanvas.height / 2; if (i === 0) { canvasCtx.moveTo(x, y); } else { canvasCtx.lineTo(x, y); } x += sliceWidth; } canvasCtx.lineTo(waveformCanvas.width, waveformCanvas.height / 2); canvasCtx.stroke(); }; draw(); console.log('Waveform drawing started'); audioChunks = []; mediaRecorder = new MediaRecorder(stream); mediaRecorder.ondataavailable = event => { console.log('Audio data available, chunk size:', event.data.size); audioChunks.push(event.data); }; mediaRecorder.onstop = () => { console.log('MediaRecorder stopped and onstop event fired.'); if (streamReference) { streamReference.getTracks().forEach(track => track.stop()); console.log('Microphone stream tracks stopped.'); } startRecordBtn.disabled = false; stopRecordBtn.disabled = true; saveRecordBtn.style.display = 'inline-block'; console.log('Save button is now visible'); cancelAnimationFrame(animationFrameId); if (audioContext && audioContext.state !== 'closed') { audioContext.close().then(() => console.log('AudioContext closed')); } }; mediaRecorder.start(); console.log('MediaRecorder started'); } catch (err) { console.error('Error starting recording:', err); alert('Could not start recording. Please ensure you have a microphone and have granted permission.'); startRecordBtn.disabled = false; stopRecordBtn.disabled = true; } }); stopRecordBtn.addEventListener('click', () => { console.log('Stop recording button clicked'); if (mediaRecorder && mediaRecorder.state === 'recording') { mediaRecorder.stop(); } else { console.warn('Stop button clicked but mediaRecorder is not recording.'); } }); saveRecordBtn.addEventListener('click', () => { console.log('Save recording button clicked'); if (audioChunks.length === 0) { console.error('No audio chunks to save.'); alert('There is no audio to save.'); return; } const audioBlob = new Blob(audioChunks, { type: 'audio/wav' }); const formData = new FormData(); formData.append('audio_data', audioBlob, 'recording.wav'); console.log('Sending audio data to server. Blob size:', audioBlob.size); fetch('save_audio.php', { method: 'POST', body: formData }) .then(response => { console.log('Received response from save_audio.php'); if (!response.ok) { throw new Error(`Server responded with ${response.status}`); } return response.json(); }) .then(data => { console.log('Server response:', data); if (data.success) { alert('Recording saved!'); saveRecordBtn.style.display = 'none'; loadRecordings(); } else { const errorMessage = data.error || 'An unknown error occurred.'; alert('Error saving recording: ' + errorMessage); console.error('Server-side save error:', errorMessage); } }) .catch(error => { console.error('Error saving recording:', error); alert('An error occurred while saving the recording. Check the console for details.'); }); }); loadRecordings(); });