parakeet-v3-streaming / source /microphone-test.html
andito's picture
andito HF Staff
Add microphone selector, audio level meter, and fix Chrome compatibility
5151e54
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Microphone Test</title>
<style>
body {
font-family: monospace;
padding: 20px;
max-width: 800px;
margin: 0 auto;
}
button {
padding: 10px 20px;
font-size: 16px;
margin: 10px 0;
}
#log {
background: #000;
color: #0f0;
padding: 10px;
font-size: 12px;
height: 400px;
overflow-y: scroll;
margin-top: 20px;
}
#meter {
width: 100%;
height: 40px;
background: #222;
margin-top: 10px;
position: relative;
}
#meter-bar {
height: 100%;
background: linear-gradient(to right, green, yellow, red);
width: 0%;
transition: width 0.05s;
}
</style>
</head>
<body>
<h1>Microphone Test</h1>
<p>This tests if your microphone is working with Web Audio API</p>
<label for="deviceSelect">Select Microphone:</label>
<select id="deviceSelect" style="width: 100%; padding: 8px; margin: 10px 0; background: #222; color: #0f0; border: 1px solid #0f0; font-family: monospace;">
<option value="">Default Microphone</option>
</select>
<button id="start">Start Microphone Test</button>
<button id="stop" disabled>Stop Test</button>
<div id="meter">
<div id="meter-bar"></div>
</div>
<div id="log"></div>
<script>
const logEl = document.getElementById('log');
const meterBar = document.getElementById('meter-bar');
const startBtn = document.getElementById('start');
const stopBtn = document.getElementById('stop');
const deviceSelect = document.getElementById('deviceSelect');
let audioContext = null;
let source = null;
let analyser = null;
let processor = null;
let animationId = null;
// Enumerate devices on page load
async function loadDevices() {
try {
const devices = await navigator.mediaDevices.enumerateDevices();
const audioInputs = devices.filter(d => d.kind === 'audioinput');
deviceSelect.innerHTML = '<option value="">Default Microphone</option>';
audioInputs.forEach(device => {
const option = document.createElement('option');
option.value = device.deviceId;
option.textContent = device.label || `Microphone ${device.deviceId.slice(0, 8)}...`;
deviceSelect.appendChild(option);
});
console.log('Available devices:', audioInputs);
} catch (error) {
console.error('Failed to enumerate devices:', error);
}
}
loadDevices();
function log(msg) {
const line = document.createElement('div');
line.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`;
logEl.appendChild(line);
logEl.scrollTop = logEl.scrollHeight;
console.log(msg);
}
async function startTest() {
try {
const selectedDeviceId = deviceSelect.value;
log(`Requesting microphone access... ${selectedDeviceId ? `(Device: ${deviceSelect.options[deviceSelect.selectedIndex].text})` : '(Default)'}`);
const audioConstraints = {
channelCount: 1,
echoCancellation: false,
noiseSuppression: false,
autoGainControl: false,
};
if (selectedDeviceId) {
audioConstraints.deviceId = { exact: selectedDeviceId };
}
const stream = await navigator.mediaDevices.getUserMedia({
audio: audioConstraints
});
log('✓ Microphone access granted');
// Refresh device list now that we have permission
await loadDevices();
const tracks = stream.getAudioTracks();
log(`Stream has ${tracks.length} audio tracks`);
if (tracks.length > 0) {
const track = tracks[0];
const settings = track.getSettings();
log(`Track: ${track.label}`);
log(`Settings: ${JSON.stringify(settings, null, 2)}`);
log(`Enabled: ${track.enabled}, Muted: ${track.muted}, State: ${track.readyState}`);
}
audioContext = new AudioContext();
log(`AudioContext created: ${audioContext.sampleRate}Hz, state: ${audioContext.state}`);
if (audioContext.state === 'suspended') {
await audioContext.resume();
log(`AudioContext resumed to: ${audioContext.state}`);
}
source = audioContext.createMediaStreamSource(stream);
log('MediaStreamSource created');
// Test 1: AnalyserNode
analyser = audioContext.createAnalyser();
analyser.fftSize = 2048;
source.connect(analyser);
log('AnalyserNode connected');
const dataArray = new Uint8Array(analyser.frequencyBinCount);
function checkAnalyser() {
analyser.getByteTimeDomainData(dataArray);
let sum = 0;
let max = 0;
for (let i = 0; i < dataArray.length; i++) {
const val = Math.abs(dataArray[i] - 128);
sum += val;
max = Math.max(max, val);
}
const avg = sum / dataArray.length;
const percent = (max / 128) * 100;
meterBar.style.width = percent + '%';
log(`Analyser - Avg: ${avg.toFixed(2)}, Max: ${max}, Samples: ${dataArray.length}`);
if (avg < 0.1) {
log('⚠️ WARNING: Audio level is 0 or very quiet!');
}
}
setTimeout(checkAnalyser, 500);
setTimeout(checkAnalyser, 1000);
setTimeout(checkAnalyser, 2000);
// Test 2: ScriptProcessorNode
processor = audioContext.createScriptProcessor(4096, 1, 1);
source.connect(processor);
processor.connect(audioContext.destination);
let chunkCount = 0;
processor.onaudioprocess = (event) => {
const inputData = event.inputBuffer.getChannelData(0);
const max = Math.max(...Array.from(inputData).map(Math.abs));
const avg = Array.from(inputData).reduce((sum, val) => sum + Math.abs(val), 0) / inputData.length;
chunkCount++;
if (chunkCount % 10 === 0) {
log(`ScriptProcessor - Chunk ${chunkCount}, Avg: ${avg.toFixed(6)}, Max: ${max.toFixed(6)}, Length: ${inputData.length}`);
if (max < 0.0001) {
log('⚠️ WARNING: ScriptProcessor getting all zeros!');
}
}
// Update meter
const percent = (max * 100);
meterBar.style.width = Math.min(100, percent) + '%';
};
log('ScriptProcessorNode connected and listening...');
log('✓ Test running - speak into your microphone!');
startBtn.disabled = true;
stopBtn.disabled = false;
deviceSelect.disabled = true;
} catch (error) {
log(`❌ ERROR: ${error.message}`);
console.error(error);
}
}
function stopTest() {
if (processor) processor.disconnect();
if (analyser) analyser.disconnect();
if (source) source.disconnect();
if (audioContext) audioContext.close();
if (animationId) cancelAnimationFrame(animationId);
log('Test stopped');
startBtn.disabled = false;
stopBtn.disabled = true;
deviceSelect.disabled = false;
meterBar.style.width = '0%';
}
startBtn.addEventListener('click', startTest);
stopBtn.addEventListener('click', stopTest);
</script>
</body>
</html>