diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000..eaa8a8d --- /dev/null +++ b/assets/css/custom.css @@ -0,0 +1,22 @@ + +body { + background-color: #F0F8FF; + font-family: 'Poppins', sans-serif; +} + +.hero { + background-image: url('https://picsum.photos/seed/wave/1200/400'); + background-size: cover; + background-position: center; + color: white; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); +} + +#waveform { + display: block; + width: 100%; + height: 200px; + background-color: #FFFFFF; + border-radius: 0.5rem; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); +} diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..578430f --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,192 @@ +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 = '

No recordings yet.

'; + } + } 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 = '#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(); +}); \ No newline at end of file diff --git a/index.php b/index.php index 6f7ffab..8cf6960 100644 --- a/index.php +++ b/index.php @@ -1,131 +1,73 @@ - - - - New Style - - - - + + + Audio Recorder & Visualizer + + + + + -
-
-

Analyzing your requirements and generating your website…

-
- Loading… -
-

AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

+ +
+

Audio Recorder & Visualizer

+
+ +
+
+
+
+
+

Record and Visualize Your Audio

+

Click the button below to start recording your microphone and see the audio visualized in real-time.

+ + + +
+
+
+
+ +
+
+
+
+
Live Audio Waveform
+ +
+
+
+
+ +
+
+
+
+
Saved Recordings
+
+
+
+
+
+ +
+
+ A modern microphone in a recording studio. +
+
+ A collection of audio waveforms. +
+
-
- + + + + + - + \ No newline at end of file diff --git a/list_recordings.php b/list_recordings.php new file mode 100644 index 0000000..75520e2 --- /dev/null +++ b/list_recordings.php @@ -0,0 +1,17 @@ + diff --git a/save_audio.php b/save_audio.php new file mode 100644 index 0000000..f39f936 --- /dev/null +++ b/save_audio.php @@ -0,0 +1,41 @@ + false, 'message' => 'Failed to create upload directory.']; + echo json_encode($response); + exit; + } +} + +// Get the raw POST data. +$audioData = file_get_contents('php://input'); + +if ($audioData) { + // Generate a unique filename. + $fileName = 'recording_' . date('Y-m-d_H-i-s') . '_' . uniqid() . '.wav'; + $filePath = $uploadDir . $fileName; + + // Save the file. + if (file_put_contents($filePath, $audioData)) { + $response['success'] = true; + $response['message'] = 'Audio saved successfully.'; + $response['file_path'] = $filePath; + } else { + $response['success'] = false; + $response['message'] = 'Error saving audio file. Check directory permissions.'; + } +} else { + $response['success'] = false; + $response['message'] = 'No audio data received.'; +} + +echo json_encode($response); +?> \ No newline at end of file