Update static/index.html
Browse files- static/index.html +70 -6
static/index.html
CHANGED
@@ -18,12 +18,13 @@
|
|
18 |
document.getElementById("recordButton").addEventListener("click", async () => {
|
19 |
if (!mediaRecorder || mediaRecorder.state === "inactive") {
|
20 |
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
21 |
-
mediaRecorder = new MediaRecorder(stream);
|
22 |
mediaRecorder.ondataavailable = event => audioChunks.push(event.data);
|
23 |
mediaRecorder.onstop = async () => {
|
24 |
-
const audioBlob = new Blob(audioChunks, { type: "audio/
|
|
|
25 |
const formData = new FormData();
|
26 |
-
formData.append("file",
|
27 |
|
28 |
const response = await fetch("/chat/", {
|
29 |
method: "POST",
|
@@ -39,17 +40,80 @@
|
|
39 |
});
|
40 |
|
41 |
document.getElementById("generateButton").addEventListener("click", async () => {
|
42 |
-
const
|
43 |
-
if (
|
44 |
const response = await fetch("/tts/", {
|
45 |
method: "POST",
|
46 |
headers: { "Content-Type": "application/json" },
|
47 |
-
body: JSON.stringify({ text
|
48 |
});
|
49 |
const audioData = await response.blob();
|
50 |
document.getElementById("audioPlayer").src = URL.createObjectURL(audioData);
|
51 |
}
|
52 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
</script>
|
54 |
</body>
|
55 |
</html>
|
|
|
18 |
document.getElementById("recordButton").addEventListener("click", async () => {
|
19 |
if (!mediaRecorder || mediaRecorder.state === "inactive") {
|
20 |
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
21 |
+
mediaRecorder = new MediaRecorder(stream, { mimeType: "audio/webm" });
|
22 |
mediaRecorder.ondataavailable = event => audioChunks.push(event.data);
|
23 |
mediaRecorder.onstop = async () => {
|
24 |
+
const audioBlob = new Blob(audioChunks, { type: "audio/webm" });
|
25 |
+
const wavBlob = await convertWebMToWav(audioBlob);
|
26 |
const formData = new FormData();
|
27 |
+
formData.append("file", wavBlob, "recording.wav");
|
28 |
|
29 |
const response = await fetch("/chat/", {
|
30 |
method: "POST",
|
|
|
40 |
});
|
41 |
|
42 |
document.getElementById("generateButton").addEventListener("click", async () => {
|
43 |
+
const text = prompt("Enter text to convert to speech:");
|
44 |
+
if (text) {
|
45 |
const response = await fetch("/tts/", {
|
46 |
method: "POST",
|
47 |
headers: { "Content-Type": "application/json" },
|
48 |
+
body: JSON.stringify({ text })
|
49 |
});
|
50 |
const audioData = await response.blob();
|
51 |
document.getElementById("audioPlayer").src = URL.createObjectURL(audioData);
|
52 |
}
|
53 |
});
|
54 |
+
|
55 |
+
async function convertWebMToWav(blob) {
|
56 |
+
return new Promise(resolve => {
|
57 |
+
const reader = new FileReader();
|
58 |
+
reader.onload = function () {
|
59 |
+
const audioContext = new AudioContext();
|
60 |
+
audioContext.decodeAudioData(reader.result, buffer => {
|
61 |
+
const wavBuffer = audioBufferToWav(buffer);
|
62 |
+
resolve(new Blob([wavBuffer], { type: "audio/wav" }));
|
63 |
+
});
|
64 |
+
};
|
65 |
+
reader.readAsArrayBuffer(blob);
|
66 |
+
});
|
67 |
+
}
|
68 |
+
|
69 |
+
function audioBufferToWav(buffer) {
|
70 |
+
let numOfChan = buffer.numberOfChannels,
|
71 |
+
length = buffer.length * numOfChan * 2 + 44,
|
72 |
+
bufferArray = new ArrayBuffer(length),
|
73 |
+
view = new DataView(bufferArray),
|
74 |
+
channels = [],
|
75 |
+
sampleRate = buffer.sampleRate,
|
76 |
+
offset = 0,
|
77 |
+
pos = 0;
|
78 |
+
|
79 |
+
setUint32(0x46464952); // "RIFF"
|
80 |
+
setUint32(length - 8);
|
81 |
+
setUint32(0x45564157); // "WAVE"
|
82 |
+
setUint32(0x20746d66); // "fmt " chunk
|
83 |
+
setUint32(16); // length = 16
|
84 |
+
setUint16(1); // PCM (uncompressed)
|
85 |
+
setUint16(numOfChan);
|
86 |
+
setUint32(sampleRate);
|
87 |
+
setUint32(sampleRate * 2 * numOfChan);
|
88 |
+
setUint16(numOfChan * 2);
|
89 |
+
setUint16(16); // bits per sample
|
90 |
+
setUint32(0x61746164); // "data" chunk
|
91 |
+
setUint32(length - pos - 4);
|
92 |
+
|
93 |
+
for (let i = 0; i < buffer.numberOfChannels; i++)
|
94 |
+
channels.push(buffer.getChannelData(i));
|
95 |
+
|
96 |
+
while (pos < length) {
|
97 |
+
for (let i = 0; i < numOfChan; i++) {
|
98 |
+
let sample = Math.max(-1, Math.min(1, channels[i][offset]));
|
99 |
+
sample = sample < 0 ? sample * 0x8000 : sample * 0x7FFF;
|
100 |
+
setUint16(sample);
|
101 |
+
}
|
102 |
+
offset++;
|
103 |
+
}
|
104 |
+
|
105 |
+
function setUint16(data) {
|
106 |
+
view.setUint16(pos, data, true);
|
107 |
+
pos += 2;
|
108 |
+
}
|
109 |
+
|
110 |
+
function setUint32(data) {
|
111 |
+
view.setUint32(pos, data, true);
|
112 |
+
pos += 4;
|
113 |
+
}
|
114 |
+
|
115 |
+
return bufferArray;
|
116 |
+
}
|
117 |
</script>
|
118 |
</body>
|
119 |
</html>
|