- index.html +81 -9
index.html
CHANGED
@@ -66,21 +66,30 @@
|
|
66 |
this.recordButton.addEventListener('click', () => this.toggleRecording());
|
67 |
|
68 |
// Replace with your Eleven Labs API key
|
69 |
-
this.ELEVEN_LABS_API_KEY = '
|
70 |
}
|
71 |
|
72 |
async setupMediaRecorder() {
|
73 |
try {
|
74 |
-
const stream = await navigator.mediaDevices.getUserMedia({
|
75 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
76 |
|
77 |
this.mediaRecorder.ondataavailable = (event) => {
|
78 |
this.audioChunks.push(event.data);
|
79 |
};
|
80 |
|
81 |
this.mediaRecorder.onstop = async () => {
|
82 |
-
const audioBlob = new Blob(this.audioChunks, { type: 'audio/
|
83 |
-
await this.
|
84 |
this.audioChunks = [];
|
85 |
};
|
86 |
|
@@ -117,22 +126,85 @@
|
|
117 |
this.recordButton.classList.remove('recording');
|
118 |
this.statusDiv.textContent = 'Processing audio...';
|
119 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
|
121 |
-
async sendToElevenLabs(
|
122 |
try {
|
123 |
const formData = new FormData();
|
124 |
-
formData.append('audio',
|
125 |
|
126 |
const response = await fetch('https://api.elevenlabs.io/v1/speech-to-text', {
|
127 |
method: 'POST',
|
128 |
headers: {
|
129 |
'xi-api-key': this.ELEVEN_LABS_API_KEY,
|
|
|
130 |
},
|
131 |
body: formData
|
132 |
});
|
133 |
|
134 |
if (!response.ok) {
|
135 |
-
|
|
|
136 |
}
|
137 |
|
138 |
const data = await response.json();
|
@@ -141,7 +213,7 @@
|
|
141 |
|
142 |
} catch (error) {
|
143 |
console.error('Error sending to Eleven Labs:', error);
|
144 |
-
this.statusDiv.textContent = 'Error
|
145 |
}
|
146 |
}
|
147 |
}
|
|
|
66 |
this.recordButton.addEventListener('click', () => this.toggleRecording());
|
67 |
|
68 |
// Replace with your Eleven Labs API key
|
69 |
+
this.ELEVEN_LABS_API_KEY = 'sk_d31f32025dda541dcd4f0aad95d3c7d1b31d36db7116dc1f';
|
70 |
}
|
71 |
|
72 |
async setupMediaRecorder() {
|
73 |
try {
|
74 |
+
const stream = await navigator.mediaDevices.getUserMedia({
|
75 |
+
audio: {
|
76 |
+
sampleRate: 16000,
|
77 |
+
channelCount: 1
|
78 |
+
}
|
79 |
+
});
|
80 |
+
|
81 |
+
this.mediaRecorder = new MediaRecorder(stream, {
|
82 |
+
mimeType: 'audio/webm',
|
83 |
+
audioBitsPerSecond: 128000
|
84 |
+
});
|
85 |
|
86 |
this.mediaRecorder.ondataavailable = (event) => {
|
87 |
this.audioChunks.push(event.data);
|
88 |
};
|
89 |
|
90 |
this.mediaRecorder.onstop = async () => {
|
91 |
+
const audioBlob = new Blob(this.audioChunks, { type: 'audio/webm' });
|
92 |
+
await this.convertAndSend(audioBlob);
|
93 |
this.audioChunks = [];
|
94 |
};
|
95 |
|
|
|
126 |
this.recordButton.classList.remove('recording');
|
127 |
this.statusDiv.textContent = 'Processing audio...';
|
128 |
}
|
129 |
+
|
130 |
+
async convertAndSend(audioBlob) {
|
131 |
+
try {
|
132 |
+
// Convert to audio buffer
|
133 |
+
const arrayBuffer = await audioBlob.arrayBuffer();
|
134 |
+
const audioContext = new AudioContext();
|
135 |
+
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
|
136 |
+
|
137 |
+
// Create WAV file
|
138 |
+
const wavBuffer = this.audioBufferToWav(audioBuffer);
|
139 |
+
const wavBlob = new Blob([wavBuffer], { type: 'audio/wav' });
|
140 |
+
|
141 |
+
await this.sendToElevenLabs(wavBlob);
|
142 |
+
} catch (error) {
|
143 |
+
console.error('Error converting audio:', error);
|
144 |
+
this.statusDiv.textContent = 'Error converting audio. Please try again.';
|
145 |
+
}
|
146 |
+
}
|
147 |
+
|
148 |
+
audioBufferToWav(audioBuffer) {
|
149 |
+
const numberOfChannels = 1; // Mono
|
150 |
+
const sampleRate = 16000;
|
151 |
+
const format = 1; // PCM
|
152 |
+
const bitDepth = 16;
|
153 |
+
|
154 |
+
const length = audioBuffer.length * numberOfChannels * (bitDepth / 8);
|
155 |
+
const buffer = new ArrayBuffer(44 + length);
|
156 |
+
const view = new DataView(buffer);
|
157 |
+
|
158 |
+
// Write WAV header
|
159 |
+
const writeString = (view, offset, string) => {
|
160 |
+
for (let i = 0; i < string.length; i++) {
|
161 |
+
view.setUint8(offset + i, string.charCodeAt(i));
|
162 |
+
}
|
163 |
+
};
|
164 |
+
|
165 |
+
writeString(view, 0, 'RIFF');
|
166 |
+
view.setUint32(4, 36 + length, true);
|
167 |
+
writeString(view, 8, 'WAVE');
|
168 |
+
writeString(view, 12, 'fmt ');
|
169 |
+
view.setUint32(16, 16, true);
|
170 |
+
view.setUint16(20, format, true);
|
171 |
+
view.setUint16(22, numberOfChannels, true);
|
172 |
+
view.setUint32(24, sampleRate, true);
|
173 |
+
view.setUint32(28, sampleRate * numberOfChannels * (bitDepth / 8), true);
|
174 |
+
view.setUint16(32, numberOfChannels * (bitDepth / 8), true);
|
175 |
+
view.setUint16(34, bitDepth, true);
|
176 |
+
writeString(view, 36, 'data');
|
177 |
+
view.setUint32(40, length, true);
|
178 |
+
|
179 |
+
// Write audio data
|
180 |
+
const channelData = audioBuffer.getChannelData(0);
|
181 |
+
let offset = 44;
|
182 |
+
for (let i = 0; i < channelData.length; i++) {
|
183 |
+
const sample = Math.max(-1, Math.min(1, channelData[i]));
|
184 |
+
view.setInt16(offset, sample < 0 ? sample * 0x8000 : sample * 0x7FFF, true);
|
185 |
+
offset += 2;
|
186 |
+
}
|
187 |
+
|
188 |
+
return buffer;
|
189 |
+
}
|
190 |
|
191 |
+
async sendToElevenLabs(wavBlob) {
|
192 |
try {
|
193 |
const formData = new FormData();
|
194 |
+
formData.append('audio', wavBlob, 'recording.wav');
|
195 |
|
196 |
const response = await fetch('https://api.elevenlabs.io/v1/speech-to-text', {
|
197 |
method: 'POST',
|
198 |
headers: {
|
199 |
'xi-api-key': this.ELEVEN_LABS_API_KEY,
|
200 |
+
'Accept': 'application/json'
|
201 |
},
|
202 |
body: formData
|
203 |
});
|
204 |
|
205 |
if (!response.ok) {
|
206 |
+
const errorText = await response.text();
|
207 |
+
throw new Error(`HTTP error! status: ${response.status}, ${errorText}`);
|
208 |
}
|
209 |
|
210 |
const data = await response.json();
|
|
|
213 |
|
214 |
} catch (error) {
|
215 |
console.error('Error sending to Eleven Labs:', error);
|
216 |
+
this.statusDiv.textContent = 'Error sending to Eleven Labs API. Please check your API key and try again.';
|
217 |
}
|
218 |
}
|
219 |
}
|