audio-recorder / index.html
NeoPy's picture
Add 3 files
03e2aca verified
raw
history blame
17.3 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Online Audio Recorder</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.waveform {
background: linear-gradient(90deg, #3b82f6 0%, #8b5cf6 50%, #ec4899 100%);
height: 100px;
border-radius: 0.5rem;
position: relative;
overflow: hidden;
}
.waveform::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg,
rgba(255,255,255,0.1) 0%,
rgba(255,255,255,0.3) 50%,
rgba(255,255,255,0.1) 100%);
animation: wave 2s linear infinite;
transform: translateX(-100%);
}
@keyframes wave {
100% {
transform: translateX(100%);
}
}
.recording-animation {
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(239, 68, 68, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0);
}
}
.format-option:hover {
transform: translateY(-3px);
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
}
.format-option {
transition: all 0.3s ease;
}
</style>
</head>
<body class="bg-gray-100 min-h-screen">
<div class="container mx-auto px-4 py-8">
<div class="max-w-3xl mx-auto">
<div class="text-center mb-8">
<h1 class="text-4xl font-bold text-gray-800 mb-2">Online Audio Recorder</h1>
<p class="text-gray-600">Record, play and download high-quality audio in multiple formats</p>
</div>
<div class="bg-white rounded-xl shadow-lg overflow-hidden mb-8">
<div class="p-6">
<div class="waveform mb-6" id="waveform"></div>
<div class="flex flex-col sm:flex-row justify-center items-center gap-4 mb-6">
<button id="recordBtn" class="bg-red-500 hover:bg-red-600 text-white font-bold py-3 px-6 rounded-full flex items-center gap-2 transition-all">
<i class="fas fa-microphone"></i> Start Recording
</button>
<button id="stopBtn" disabled class="bg-gray-300 text-gray-600 font-bold py-3 px-6 rounded-full flex items-center gap-2 transition-all">
<i class="fas fa-stop"></i> Stop
</button>
<button id="playBtn" disabled class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-3 px-6 rounded-full flex items-center gap-2 transition-all">
<i class="fas fa-play"></i> Play
</button>
</div>
<div class="text-center text-gray-500 mb-4">
<span id="timer">00:00</span>
</div>
</div>
<div class="bg-gray-50 p-6 border-t border-gray-200">
<h3 class="text-lg font-semibold text-gray-800 mb-4">Download Options</h3>
<div class="grid grid-cols-2 sm:grid-cols-4 gap-4">
<div class="format-option bg-white p-4 rounded-lg border border-gray-200 text-center cursor-pointer" data-format="wav">
<i class="fas fa-file-audio text-3xl text-blue-500 mb-2"></i>
<p class="font-medium">WAV</p>
<p class="text-sm text-gray-500">High quality</p>
</div>
<div class="format-option bg-white p-4 rounded-lg border border-gray-200 text-center cursor-pointer" data-format="mp3">
<i class="fas fa-file-audio text-3xl text-purple-500 mb-2"></i>
<p class="font-medium">MP3</p>
<p class="text-sm text-gray-500">Compressed</p>
</div>
<div class="format-option bg-white p-4 rounded-lg border border-gray-200 text-center cursor-pointer" data-format="ogg">
<i class="fas fa-file-audio text-3xl text-green-500 mb-2"></i>
<p class="font-medium">OGG</p>
<p class="text-sm text-gray-500">Web optimized</p>
</div>
<div class="format-option bg-white p-4 rounded-lg border border-gray-200 text-center cursor-pointer" data-format="aac">
<i class="fas fa-file-audio text-3xl text-red-500 mb-2"></i>
<p class="font-medium">AAC</p>
<p class="text-sm text-gray-500">Mobile friendly</p>
</div>
</div>
<button id="downloadBtn" disabled class="mt-6 w-full bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-3 px-4 rounded-lg flex items-center justify-center gap-2 transition-all">
<i class="fas fa-download"></i> Download Audio
</button>
</div>
</div>
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
<div class="p-6">
<h3 class="text-lg font-semibold text-gray-800 mb-4">Recording History</h3>
<div id="recordingsList" class="space-y-3">
<div class="text-center text-gray-500 py-8">
<i class="fas fa-microphone-slash text-3xl mb-2"></i>
<p>No recordings yet</p>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Audio recording variables
let mediaRecorder;
let audioChunks = [];
let audioBlob;
let audioUrl;
let audioElement;
let timerInterval;
let seconds = 0;
// DOM elements
const recordBtn = document.getElementById('recordBtn');
const stopBtn = document.getElementById('stopBtn');
const playBtn = document.getElementById('playBtn');
const downloadBtn = document.getElementById('downloadBtn');
const timer = document.getElementById('timer');
const recordingsList = document.getElementById('recordingsList');
const formatOptions = document.querySelectorAll('.format-option');
let selectedFormat = 'wav'; // Default format
// Format selection
formatOptions.forEach(option => {
option.addEventListener('click', function() {
formatOptions.forEach(opt => opt.classList.remove('ring-2', 'ring-indigo-500'));
this.classList.add('ring-2', 'ring-indigo-500');
selectedFormat = this.dataset.format;
});
});
// Set first format as selected by default
formatOptions[0].classList.add('ring-2', 'ring-indigo-500');
// Request permission to use microphone
recordBtn.addEventListener('click', async function() {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream);
audioChunks = [];
mediaRecorder.ondataavailable = function(event) {
audioChunks.push(event.data);
};
mediaRecorder.onstop = function() {
audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
audioUrl = URL.createObjectURL(audioBlob);
// Create audio element for playback
audioElement = new Audio(audioUrl);
// Enable play and download buttons
playBtn.disabled = false;
downloadBtn.disabled = false;
// Add to recordings list
addRecordingToList(audioUrl);
};
mediaRecorder.start(10); // Collect data every 10ms
// Start timer
seconds = 0;
updateTimer();
timerInterval = setInterval(updateTimer, 1000);
// Update UI
recordBtn.disabled = true;
recordBtn.classList.remove('bg-red-500', 'hover:bg-red-600');
recordBtn.classList.add('bg-gray-300', 'text-gray-600');
stopBtn.disabled = false;
stopBtn.classList.remove('bg-gray-300', 'text-gray-600');
stopBtn.classList.add('bg-gray-800', 'hover:bg-gray-900', 'text-white');
// Add recording animation
recordBtn.classList.add('recording-animation');
} catch (error) {
console.error('Error accessing microphone:', error);
alert('Could not access microphone. Please check permissions.');
}
});
// Stop recording
stopBtn.addEventListener('click', function() {
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
mediaRecorder.stop();
// Stop all tracks in the stream
mediaRecorder.stream.getTracks().forEach(track => track.stop());
// Clear timer
clearInterval(timerInterval);
// Update UI
recordBtn.disabled = false;
recordBtn.classList.remove('bg-gray-300', 'text-gray-600');
recordBtn.classList.add('bg-red-500', 'hover:bg-red-600');
stopBtn.disabled = true;
stopBtn.classList.remove('bg-gray-800', 'hover:bg-gray-900', 'text-white');
stopBtn.classList.add('bg-gray-300', 'text-gray-600');
// Remove recording animation
recordBtn.classList.remove('recording-animation');
}
});
// Play recording
playBtn.addEventListener('click', function() {
if (audioElement) {
if (audioElement.paused) {
audioElement.play();
playBtn.innerHTML = '<i class="fas fa-pause"></i> Pause';
} else {
audioElement.pause();
playBtn.innerHTML = '<i class="fas fa-play"></i> Play';
}
}
});
// Download recording
downloadBtn.addEventListener('click', function() {
if (audioBlob) {
const a = document.createElement('a');
a.style.display = 'none';
a.href = audioUrl;
a.download = `recording_${new Date().toISOString().slice(0, 19).replace(/[:T-]/g, '_')}.${selectedFormat}`;
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
window.URL.revokeObjectURL(audioUrl);
}, 100);
}
});
// Update timer display
function updateTimer() {
seconds++;
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
timer.textContent = `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
}
// Add recording to history list
function addRecordingToList(url) {
// Remove placeholder if it exists
if (recordingsList.querySelector('.text-center')) {
recordingsList.innerHTML = '';
}
const recordingItem = document.createElement('div');
recordingItem.className = 'bg-gray-50 p-4 rounded-lg border border-gray-200 flex items-center justify-between';
recordingItem.innerHTML = `
<div class="flex items-center gap-3">
<i class="fas fa-microphone text-indigo-500"></i>
<div>
<p class="font-medium">Recording ${recordingsList.children.length + 1}</p>
<p class="text-sm text-gray-500">${new Date().toLocaleString()}</p>
</div>
</div>
<div class="flex gap-2">
<button class="play-history-btn bg-blue-500 hover:bg-blue-600 text-white p-2 rounded-full">
<i class="fas fa-play text-xs"></i>
</button>
<button class="download-history-btn bg-indigo-500 hover:bg-indigo-600 text-white p-2 rounded-full">
<i class="fas fa-download text-xs"></i>
</button>
</div>
`;
recordingsList.prepend(recordingItem);
// Add event listeners to the new buttons
const playBtn = recordingItem.querySelector('.play-history-btn');
const downloadBtn = recordingItem.querySelector('.download-history-btn');
const audio = new Audio(url);
playBtn.addEventListener('click', function() {
if (audio.paused) {
// Stop all other audio elements first
document.querySelectorAll('audio').forEach(a => {
if (a !== audio) a.pause();
});
audio.play();
playBtn.innerHTML = '<i class="fas fa-pause text-xs"></i>';
} else {
audio.pause();
playBtn.innerHTML = '<i class="fas fa-play text-xs"></i>';
}
});
downloadBtn.addEventListener('click', function() {
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = `recording_${new Date().toISOString().slice(0, 19).replace(/[:T-]/g, '_')}.${selectedFormat}`;
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
}, 100);
});
// Reset play button when audio ends
audio.addEventListener('ended', function() {
playBtn.innerHTML = '<i class="fas fa-play text-xs"></i>';
});
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=NeoPy/audio-recorder" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>