Spaces:
Running
Running
<html lang="pt-BR"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Microphone Checker | Teste seu Microfone</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 { | |
height: 80px; | |
background: linear-gradient(90deg, #3b82f6, #8b5cf6); | |
position: relative; | |
overflow: hidden; | |
} | |
.waveform::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 80' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 40 Q 25 10, 50 40 T 100 40 T 150 40 T 200 40' stroke='%23ffffff10' fill='none' stroke-width='2'/%3E%3C/svg%3E"); | |
opacity: 0.3; | |
} | |
.audio-level { | |
height: 100%; | |
width: 0; | |
background-color: rgba(255, 255, 255, 0.2); | |
transition: width 0.05s ease-out; | |
} | |
@keyframes pulse { | |
0% { transform: scale(1); } | |
50% { transform: scale(1.05); } | |
100% { transform: scale(1); } | |
} | |
.pulse { | |
animation: pulse 2s infinite; | |
} | |
</style> | |
</head> | |
<body class="bg-gradient-to-br from-blue-50 to-purple-50 min-h-screen"> | |
<div class="container mx-auto px-4 py-12"> | |
<div class="max-w-3xl mx-auto"> | |
<!-- Header --> | |
<div class="text-center mb-12"> | |
<h1 class="text-4xl font-bold text-gray-800 mb-2">Microphone Checker</h1> | |
<p class="text-lg text-gray-600">Verifique e teste seus dispositivos de microfone</p> | |
</div> | |
<!-- Main Card --> | |
<div class="bg-white rounded-xl shadow-xl overflow-hidden transition-all duration-300 hover:shadow-2xl"> | |
<!-- Status Section --> | |
<div class="p-8 border-b border-gray-100"> | |
<div class="flex items-center justify-between"> | |
<div> | |
<h2 class="text-2xl font-semibold text-gray-800">Status do Microfone</h2> | |
<p class="text-gray-500" id="status-text">Verificando dispositivos...</p> | |
</div> | |
<div id="status-icon" class="text-4xl text-gray-400"> | |
<i class="fas fa-microphone-slash"></i> | |
</div> | |
</div> | |
</div> | |
<!-- Device Selection --> | |
<div class="p-8 border-b border-gray-100" id="device-section" style="display: none;"> | |
<h3 class="text-lg font-medium text-gray-700 mb-4">Selecione seu microfone</h3> | |
<select id="device-select" class="w-full p-3 border border-gray-200 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> | |
<option value="">Carregando dispositivos...</option> | |
</select> | |
</div> | |
<!-- Visualizer --> | |
<div class="waveform" id="visualizer"> | |
<div class="audio-level" id="audio-level"></div> | |
</div> | |
<!-- Controls --> | |
<div class="p-8"> | |
<div class="flex flex-col sm:flex-row gap-4"> | |
<button id="test-btn" class="flex-1 bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-6 rounded-lg transition-all flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed" disabled> | |
<i class="fas fa-play"></i> Testar Microfone | |
</button> | |
<button id="stop-btn" class="flex-1 bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-3 px-6 rounded-lg transition-all flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed" disabled> | |
<i class="fas fa-stop"></i> Parar Teste | |
</button> | |
</div> | |
<div class="mt-8 bg-blue-50 border border-blue-100 rounded-lg p-4" id="instructions"> | |
<h3 class="font-medium text-blue-800 mb-2 flex items-center gap-2"> | |
<i class="fas fa-info-circle"></i> Instruções | |
</h3> | |
<ol class="list-decimal list-inside text-blue-700 space-y-1"> | |
<li>Permita o acesso ao microfone quando solicitado</li> | |
<li>Selecione seu dispositivo de microfone</li> | |
<li>Clique em "Testar Microfone" e comece a falar</li> | |
<li>O visualizador mostrará o nível de entrada de áudio</li> | |
</ol> | |
</div> | |
</div> | |
</div> | |
<!-- Footer --> | |
<div class="mt-12 text-center text-gray-500 text-sm"> | |
<p>© 2023 Microphone Checker | Desenvolvido com <i class="fas fa-heart text-red-500"></i> para testar seus dispositivos de áudio</p> | |
</div> | |
</div> | |
</div> | |
<script> | |
document.addEventListener('DOMContentLoaded', () => { | |
const statusText = document.getElementById('status-text'); | |
const statusIcon = document.getElementById('status-icon'); | |
const deviceSection = document.getElementById('device-section'); | |
const deviceSelect = document.getElementById('device-select'); | |
const testBtn = document.getElementById('test-btn'); | |
const stopBtn = document.getElementById('stop-btn'); | |
const audioLevel = document.getElementById('audio-level'); | |
const visualizer = document.getElementById('visualizer'); | |
const instructions = document.getElementById('instructions'); | |
let audioContext; | |
let microphone; | |
let analyser; | |
let isTesting = false; | |
let devices = []; | |
// Check microphone availability | |
checkMicrophoneAvailability(); | |
async function checkMicrophoneAvailability() { | |
try { | |
// Request permission to list devices | |
const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); | |
stream.getTracks().forEach(track => track.stop()); | |
// List audio devices | |
devices = await navigator.mediaDevices.enumerateDevices(); | |
const audioDevices = devices.filter(device => device.kind === 'audioinput'); | |
if (audioDevices.length > 0) { | |
// Update status | |
statusText.textContent = `${audioDevices.length} microfone(s) disponível(is)`; | |
statusIcon.innerHTML = '<i class="fas fa-microphone text-green-500 pulse"></i>'; | |
// Show device selection | |
deviceSection.style.display = 'block'; | |
populateDeviceSelect(audioDevices); | |
// Enable test button | |
testBtn.disabled = false; | |
} else { | |
statusText.textContent = 'Nenhum microfone encontrado'; | |
statusIcon.innerHTML = '<i class="fas fa-microphone-slash text-red-500"></i>'; | |
} | |
} catch (error) { | |
console.error('Error accessing microphone:', error); | |
statusText.textContent = 'Acesso ao microfone negado ou erro ao acessar'; | |
statusIcon.innerHTML = '<i class="fas fa-microphone-slash text-red-500"></i>'; | |
// Update instructions | |
instructions.querySelector('ol').innerHTML = ` | |
<li class="text-red-500">Permissão para acessar o microfone foi negada</li> | |
<li>Atualize a página e permita o acesso ao microfone</li> | |
<li>Verifique as configurações de privacidade do seu navegador</li> | |
`; | |
} | |
} | |
function populateDeviceSelect(audioDevices) { | |
deviceSelect.innerHTML = ''; | |
if (audioDevices.length === 0) { | |
deviceSelect.innerHTML = '<option value="">Nenhum microfone encontrado</option>'; | |
return; | |
} | |
// Add default option | |
const defaultOption = document.createElement('option'); | |
defaultOption.value = ''; | |
defaultOption.textContent = 'Selecione um microfone...'; | |
deviceSelect.appendChild(defaultOption); | |
// Add devices | |
audioDevices.forEach(device => { | |
const option = document.createElement('option'); | |
option.value = device.deviceId; | |
option.textContent = device.label || `Microfone ${deviceSelect.options.length}`; | |
deviceSelect.appendChild(option); | |
}); | |
} | |
testBtn.addEventListener('click', startTest); | |
stopBtn.addEventListener('click', stopTest); | |
async function startTest() { | |
if (isTesting) return; | |
const deviceId = deviceSelect.value; | |
if (!deviceId) { | |
alert('Por favor, selecione um microfone primeiro'); | |
return; | |
} | |
try { | |
isTesting = true; | |
testBtn.disabled = true; | |
stopBtn.disabled = false; | |
visualizer.style.background = 'linear-gradient(90deg, #10b981, #3b82f6)'; | |
// Initialize audio context | |
audioContext = new (window.AudioContext || window.webkitAudioContext)(); | |
analyser = audioContext.createAnalyser(); | |
analyser.fftSize = 32; | |
// Get microphone stream | |
const constraints = { | |
audio: { | |
deviceId: deviceId ? { exact: deviceId } : undefined, | |
echoCancellation: false, | |
noiseSuppression: false, | |
autoGainControl: false | |
} | |
}; | |
const stream = await navigator.mediaDevices.getUserMedia(constraints); | |
microphone = audioContext.createMediaStreamSource(stream); | |
microphone.connect(analyser); | |
// Update UI | |
statusText.textContent = 'Testando microfone... Fale agora'; | |
statusIcon.innerHTML = '<i class="fas fa-microphone text-green-500 animate-pulse"></i>'; | |
// Start visualization | |
visualize(); | |
} catch (error) { | |
console.error('Error starting test:', error); | |
statusText.textContent = 'Erro ao iniciar o teste do microfone'; | |
statusIcon.innerHTML = '<i class="fas fa-microphone-slash text-red-500"></i>'; | |
stopTest(); | |
} | |
} | |
function stopTest() { | |
if (!isTesting) return; | |
isTesting = false; | |
testBtn.disabled = false; | |
stopBtn.disabled = true; | |
visualizer.style.background = 'linear-gradient(90deg, #3b82f6, #8b5cf6)'; | |
// Stop all tracks | |
if (microphone && microphone.mediaStream) { | |
microphone.mediaStream.getTracks().forEach(track => track.stop()); | |
} | |
// Disconnect audio nodes | |
if (microphone && analyser) { | |
microphone.disconnect(); | |
} | |
// Update UI | |
statusText.textContent = 'Teste concluído'; | |
statusIcon.innerHTML = '<i class="fas fa-microphone text-green-500"></i>'; | |
audioLevel.style.width = '0'; | |
} | |
function visualize() { | |
if (!isTesting) return; | |
const dataArray = new Uint8Array(analyser.frequencyBinCount); | |
analyser.getByteFrequencyData(dataArray); | |
// Calculate average volume | |
let sum = 0; | |
for (let i = 0; i < dataArray.length; i++) { | |
sum += dataArray[i]; | |
} | |
const average = sum / dataArray.length; | |
// Update visualizer | |
audioLevel.style.width = `${average}%`; | |
// Color intensity based on volume | |
const intensity = Math.min(average / 100, 1); | |
const r = Math.floor(16 + (255 - 16) * intensity); | |
const g = Math.floor(182 + (255 - 182) * intensity); | |
const b = Math.floor(255 * intensity); | |
audioLevel.style.backgroundColor = `rgba(${r}, ${g}, ${b}, 0.5)`; | |
// Continue animation | |
requestAnimationFrame(visualize); | |
} | |
// Handle device changes | |
navigator.mediaDevices.addEventListener('devicechange', async () => { | |
devices = await navigator.mediaDevices.enumerateDevices(); | |
const audioDevices = devices.filter(device => device.kind === 'audioinput'); | |
populateDeviceSelect(audioDevices); | |
if (audioDevices.length > 0) { | |
statusText.textContent = `${audioDevices.length} microfone(s) disponível(is)`; | |
statusIcon.innerHTML = '<i class="fas fa-microphone text-green-500 pulse"></i>'; | |
} else { | |
statusText.textContent = 'Nenhum microfone encontrado'; | |
statusIcon.innerHTML = '<i class="fas fa-microphone-slash text-red-500"></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=Ravisil/teste-microphone" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body> | |
</html> |