testjs / index.html
zshashz's picture
dd
aea2245
raw
history blame
8.83 kB
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Voice Recorder</title>
<style>
.card {
max-width: 600px;
margin: 2rem auto;
padding: 2rem;
border-radius: 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
text-align: center;
}
.button {
padding: 1rem 2rem;
margin: 0.5rem;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 1rem;
}
.record {
background-color: #ff4444;
color: white;
}
.record.recording {
background-color: #aa0000;
}
.status {
margin-top: 1rem;
font-style: italic;
}
.response {
margin-top: 1rem;
padding: 1rem;
background-color: #f5f5f5;
border-radius: 5px;
}
</style>
</head>
<body>
<div class="card">
<h1>Voice Recorder</h1>
<button id="recordButton" class="button record">Start Recording</button>
<div id="status" class="status">Click to start recording</div>
<div id="response" class="response"></div>
</div>
<script>
class VoiceRecorder {
constructor() {
this.mediaRecorder = null;
this.audioChunks = [];
this.isRecording = false;
this.recordButton = document.getElementById('recordButton');
this.statusDiv = document.getElementById('status');
this.responseDiv = document.getElementById('response');
this.recordButton.addEventListener('click', () => this.toggleRecording());
// Replace with your Eleven Labs API key
this.ELEVEN_LABS_API_KEY = 'sk_f54a899fc3dd71448fc4a1e2c07f15aafa93580cdc50d853';
}
async setupMediaRecorder() {
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: {
sampleRate: 16000,
channelCount: 1
}
});
this.mediaRecorder = new MediaRecorder(stream, {
mimeType: 'audio/webm',
audioBitsPerSecond: 128000
});
this.mediaRecorder.ondataavailable = (event) => {
this.audioChunks.push(event.data);
};
this.mediaRecorder.onstop = async () => {
const audioBlob = new Blob(this.audioChunks, { type: 'audio/webm' });
await this.convertAndSend(audioBlob);
this.audioChunks = [];
};
} catch (error) {
console.error('Error accessing microphone:', error);
this.statusDiv.textContent = 'Error accessing microphone. Please ensure microphone permissions are granted.';
}
}
async toggleRecording() {
if (!this.mediaRecorder) {
await this.setupMediaRecorder();
}
if (!this.isRecording) {
this.startRecording();
} else {
this.stopRecording();
}
}
startRecording() {
this.mediaRecorder.start();
this.isRecording = true;
this.recordButton.textContent = 'Stop Recording';
this.recordButton.classList.add('recording');
this.statusDiv.textContent = 'Recording...';
}
stopRecording() {
this.mediaRecorder.stop();
this.isRecording = false;
this.recordButton.textContent = 'Start Recording';
this.recordButton.classList.remove('recording');
this.statusDiv.textContent = 'Processing audio...';
}
async convertAndSend(audioBlob) {
try {
// Convert to audio buffer
const arrayBuffer = await audioBlob.arrayBuffer();
const audioContext = new AudioContext();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
// Create WAV file
const wavBuffer = this.audioBufferToWav(audioBuffer);
const wavBlob = new Blob([wavBuffer], { type: 'audio/wav' });
await this.sendToElevenLabs(wavBlob);
} catch (error) {
console.error('Error converting audio:', error);
this.statusDiv.textContent = 'Error converting audio. Please try again.';
}
}
audioBufferToWav(audioBuffer) {
const numberOfChannels = 1; // Mono
const sampleRate = 16000;
const format = 1; // PCM
const bitDepth = 16;
const length = audioBuffer.length * numberOfChannels * (bitDepth / 8);
const buffer = new ArrayBuffer(44 + length);
const view = new DataView(buffer);
// Write WAV header
const writeString = (view, offset, string) => {
for (let i = 0; i < string.length; i++) {
view.setUint8(offset + i, string.charCodeAt(i));
}
};
writeString(view, 0, 'RIFF');
view.setUint32(4, 36 + length, true);
writeString(view, 8, 'WAVE');
writeString(view, 12, 'fmt ');
view.setUint32(16, 16, true);
view.setUint16(20, format, true);
view.setUint16(22, numberOfChannels, true);
view.setUint32(24, sampleRate, true);
view.setUint32(28, sampleRate * numberOfChannels * (bitDepth / 8), true);
view.setUint16(32, numberOfChannels * (bitDepth / 8), true);
view.setUint16(34, bitDepth, true);
writeString(view, 36, 'data');
view.setUint32(40, length, true);
// Write audio data
const channelData = audioBuffer.getChannelData(0);
let offset = 44;
for (let i = 0; i < channelData.length; i++) {
const sample = Math.max(-1, Math.min(1, channelData[i]));
view.setInt16(offset, sample < 0 ? sample * 0x8000 : sample * 0x7FFF, true);
offset += 2;
}
return buffer;
}
async sendToElevenLabs(wavBlob) {
try {
const formData = new FormData();
formData.append('file', wavBlob, 'recording.wav');
const response = await fetch('https://api.elevenlabs.io/v2/speech-recognition', {
method: 'POST',
headers: {
'xi-api-key': this.ELEVEN_LABS_API_KEY,
'Accept': 'application/json'
},
body: formData
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`HTTP error! status: ${response.status}, ${errorText}`);
}
const data = await response.json();
this.responseDiv.textContent = `Transcription: ${data.text}`;
this.statusDiv.textContent = 'Transcription complete';
} catch (error) {
console.error('Error sending to Eleven Labs:', error);
this.statusDiv.textContent = 'Error sending to Eleven Labs API. Please check your API key and try again.';
}
}
}
// Initialize the voice recorder when the page loads
window.addEventListener('load', () => {
new VoiceRecorder();
});
</script>
</body>
</html>