2025-09-23 21:45:57 +00:00

195 lines
8.1 KiB
JavaScript

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 = '<i class="bi bi-play-fill"></i>';
playBtn.onclick = () => {
const audio = new Audio(recording);
audio.play();
};
card.appendChild(nameSpan);
card.appendChild(playBtn);
list.appendChild(card);
});
recordingsList.appendChild(list);
} else {
recordingsList.innerHTML = '<div class="text-center text-muted p-4"><p>No recordings yet.</p><small>Your saved recordings will appear here.</small></div>';
}
} catch (error) {
console.error('Failed to load recordings:', error);
recordingsList.innerHTML = '<p class="text-center text-danger">Could not load recordings.</p>';
}
}
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();
});