149 |
150 | {messages.map((message, index) => (
151 |
152 |
153 | {message.content}
154 |
155 | {message.audio && (
156 |
159 | )}
160 |
161 | ))}
162 |
163 |
164 | setInputValue(e.target.value)}
167 | onKeyPress={(e) => e.key === 'Enter' && handleSendMessage()}
168 | placeholder="Type a message..."
169 | className="flex-grow"
170 | />
171 |
172 |
173 |
174 |
181 | {audioURL && (
182 |
185 | )}
186 |
187 |
188 | )
189 | }
190 |
191 | function createWavBlob(audioChunks: Float32Array[]): Blob {
192 | const sampleRate = 44100
193 | const numChannels = 1
194 | const bitsPerSample = 16
195 | const bytesPerSample = bitsPerSample / 8
196 | const blockAlign = numChannels * bytesPerSample
197 |
198 | const buffer = mergeAudioBuffers(audioChunks)
199 | const dataLength = buffer.length * bytesPerSample
200 | const wavDataLength = 36 + dataLength
201 |
202 | const headerBuffer = new ArrayBuffer(44)
203 | const view = new DataView(headerBuffer)
204 |
205 | writeString(view, 0, 'RIFF')
206 | view.setUint32(4, wavDataLength, true)
207 | writeString(view, 8, 'WAVE')
208 | writeString(view, 12, 'fmt ')
209 | view.setUint32(16, 16, true)
210 | view.setUint16(20, 1, true)
211 | view.setUint16(22, numChannels, true)
212 | view.setUint32(24, sampleRate, true)
213 | view.setUint32(28, sampleRate * blockAlign, true)
214 | view.setUint16(32, blockAlign, true)
215 | view.setUint16(34, bitsPerSample, true)
216 | writeString(view, 36, 'data')
217 | view.setUint32(40, dataLength, true)
218 |
219 | const wavBuffer = new Int16Array(headerBuffer.byteLength + dataLength)
220 | wavBuffer.set(new Int16Array(headerBuffer))
221 | wavBuffer.set(convertToInt16(buffer), headerBuffer.byteLength / 2)
222 |
223 | return new Blob([wavBuffer], { type: 'audio/wav' })
224 | }
225 |
226 | function writeString(view: DataView, offset: number, string: string) {
227 | for (let i = 0; i < string.length; i++) {
228 | view.setUint8(offset + i, string.charCodeAt(i))
229 | }
230 | }
231 |
232 | function mergeAudioBuffers(buffers: Float32Array[]): Float32Array {
233 | let totalLength = 0
234 | for (let buffer of buffers) {
235 | totalLength += buffer.length
236 | }
237 | const result = new Float32Array(totalLength)
238 | let offset = 0
239 | for (let buffer of buffers) {
240 | result.set(buffer, offset)
241 | offset += buffer.length
242 | }
243 | return result
244 | }
245 |
246 | function convertToInt16(float32Array: Float32Array): Int16Array {
247 | const int16Array = new Int16Array(float32Array.length)
248 | for (let i = 0; i < float32Array.length; i++) {
249 | const s = Math.max(-1, Math.min(1, float32Array[i]))
250 | int16Array[i] = s < 0 ? s * 0x8000 : s * 0x7FFF
251 | }
252 | return int16Array
253 | }
254 |
255 | function blobToBase64(blob: Blob): Promise