Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Simple ASR Client</title> | |
<script type="importmap"> | |
{ | |
"imports": { | |
"@xenova/transformers": "https://cdn.jsdelivr.net/npm/@xenova/[email protected]/+esm" | |
} | |
} | |
</script> | |
<style> | |
body { | |
font-family: Arial, sans-serif; | |
max-width: 800px; | |
margin: 20px auto; | |
padding: 20px; | |
} | |
#output { | |
margin-top: 20px; | |
padding: 10px; | |
border: 1px solid #ccc; | |
min-height: 100px; | |
} | |
.ready { | |
color: green; | |
font-weight: bold; | |
} | |
.loading { | |
color: orange; | |
} | |
.error { | |
color: red; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>Simple Speech Recognition</h1> | |
<div id="status" class="loading">Loading model...</div> | |
<button id="startBtn" disabled>Start Recording</button> | |
<div id="output"></div> | |
<script type="module"> | |
import { pipeline } from '@xenova/transformers'; | |
let isRecording = false; | |
let mediaRecorder = null; | |
let audioChunks = []; | |
const statusElement = document.getElementById('status'); | |
const startBtn = document.getElementById('startBtn'); | |
const output = document.getElementById('output'); | |
// Initialize the model | |
async function initModel() { | |
try { | |
console.log('Starting model loading...'); | |
statusElement.textContent = 'Loading model...'; | |
statusElement.className = 'loading'; | |
const model = await pipeline('automatic-speech-recognition', 'Xenova/whisper-tiny'); | |
console.log('Model loaded successfully!'); | |
statusElement.textContent = 'MODEL LOADED SUCCESSFULLY! Ready to record.'; | |
statusElement.className = 'ready'; | |
startBtn.disabled = false; | |
startBtn.onclick = async () => { | |
if (!isRecording) { | |
try { | |
const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); | |
mediaRecorder = new MediaRecorder(stream); | |
audioChunks = []; | |
mediaRecorder.ondataavailable = (event) => { | |
audioChunks.push(event.data); | |
}; | |
mediaRecorder.onstop = async () => { | |
const audioBlob = new Blob(audioChunks, { type: 'audio/wav' }); | |
try { | |
const result = await model(audioBlob); | |
output.textContent += result.text + ' '; | |
} catch (e) { | |
console.error('Transcription error:', e); | |
statusElement.textContent = 'Error during transcription'; | |
statusElement.className = 'error'; | |
} | |
audioChunks = []; | |
}; | |
mediaRecorder.start(1000); | |
isRecording = true; | |
startBtn.textContent = 'Stop Recording'; | |
statusElement.textContent = 'Recording...'; | |
statusElement.className = 'loading'; | |
} catch (e) { | |
console.error('Recording error:', e); | |
statusElement.textContent = 'Error accessing microphone'; | |
statusElement.className = 'error'; | |
} | |
} else { | |
mediaRecorder.stop(); | |
mediaRecorder.stream.getTracks().forEach(track => track.stop()); | |
isRecording = false; | |
startBtn.textContent = 'Start Recording'; | |
statusElement.textContent = 'Processing...'; | |
statusElement.className = 'loading'; | |
} | |
}; | |
} catch (e) { | |
console.error('Model loading error:', e); | |
statusElement.textContent = 'Error loading model: ' + e.message; | |
statusElement.className = 'error'; | |
} | |
} | |
// Start loading the model | |
initModel(); | |
</script> | |
</body> | |
</html> |