192 lines
7.9 KiB
JavaScript
192 lines
7.9 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('ul');
|
|
list.className = 'list-group';
|
|
recordings.forEach(recording => {
|
|
const listItem = document.createElement('li');
|
|
listItem.className = 'list-group-item d-flex justify-content-between align-items-center';
|
|
|
|
// Extract just the filename
|
|
const fileName = recording.split('/').pop();
|
|
listItem.innerText = fileName;
|
|
|
|
const audio = new Audio(recording);
|
|
const playBtn = document.createElement('button');
|
|
playBtn.className = 'btn btn-primary btn-sm';
|
|
playBtn.innerText = 'Play';
|
|
playBtn.onclick = () => audio.play();
|
|
|
|
listItem.appendChild(playBtn);
|
|
list.appendChild(listItem);
|
|
});
|
|
recordingsList.appendChild(list);
|
|
} else {
|
|
recordingsList.innerHTML = '<p class="text-center">No recordings yet.</p>';
|
|
}
|
|
} 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 = '#F0F8FF';
|
|
canvasCtx.fillRect(0, 0, waveformCanvas.width, waveformCanvas.height);
|
|
canvasCtx.lineWidth = 2;
|
|
canvasCtx.strokeStyle = '#1E90FF';
|
|
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.');
|
|
// Stop all tracks on the stream to release the microphone
|
|
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 {
|
|
// Use the error from the JSON if available, otherwise a generic message
|
|
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();
|
|
}); |