Spaces:
Runtime error
Runtime error
Delete record.js
Browse files
record.js
DELETED
@@ -1,130 +0,0 @@
|
|
1 |
-
// Shim for Safari.
|
2 |
-
window.AudioContext = window.AudioContext || window.webkitAudioContext
|
3 |
-
|
4 |
-
function audioBufferToWav(buffer, opt) {
|
5 |
-
opt = opt || {}
|
6 |
-
var numChannels = buffer.numberOfChannels
|
7 |
-
var sampleRate = buffer.sampleRate
|
8 |
-
var format = opt.float32 ? 3 : 1
|
9 |
-
var bitDepth = format === 3 ? 32 : 16
|
10 |
-
var result
|
11 |
-
if (numChannels === 2) {
|
12 |
-
result = interleave(buffer.getChannelData(0), buffer.getChannelData(1))
|
13 |
-
} else {
|
14 |
-
result = buffer.getChannelData(0)
|
15 |
-
}
|
16 |
-
return encodeWAV(result, format, sampleRate, numChannels, bitDepth)
|
17 |
-
}
|
18 |
-
|
19 |
-
function encodeWAV(samples, format, sampleRate, numChannels, bitDepth) {
|
20 |
-
var bytesPerSample = bitDepth / 8
|
21 |
-
var blockAlign = numChannels * bytesPerSample
|
22 |
-
var buffer = new ArrayBuffer(44 + samples.length * bytesPerSample)
|
23 |
-
var view = new DataView(buffer)
|
24 |
-
/* RIFF identifier */
|
25 |
-
writeString(view, 0, 'RIFF')
|
26 |
-
/* RIFF chunk length */
|
27 |
-
view.setUint32(4, 36 + samples.length * bytesPerSample, true)
|
28 |
-
/* RIFF type */
|
29 |
-
writeString(view, 8, 'WAVE')
|
30 |
-
/* format chunk identifier */
|
31 |
-
writeString(view, 12, 'fmt ')
|
32 |
-
/* format chunk length */
|
33 |
-
view.setUint32(16, 16, true)
|
34 |
-
/* sample format (raw) */
|
35 |
-
view.setUint16(20, format, true)
|
36 |
-
/* channel count */
|
37 |
-
view.setUint16(22, numChannels, true)
|
38 |
-
/* sample rate */
|
39 |
-
view.setUint32(24, sampleRate, true)
|
40 |
-
/* byte rate (sample rate * block align) */
|
41 |
-
view.setUint32(28, sampleRate * blockAlign, true)
|
42 |
-
/* block align (channel count * bytes per sample) */
|
43 |
-
view.setUint16(32, blockAlign, true)
|
44 |
-
/* bits per sample */
|
45 |
-
view.setUint16(34, bitDepth, true)
|
46 |
-
/* data chunk identifier */
|
47 |
-
writeString(view, 36, 'data')
|
48 |
-
/* data chunk length */
|
49 |
-
view.setUint32(40, samples.length * bytesPerSample, true)
|
50 |
-
if (format === 1) { // Raw PCM
|
51 |
-
floatTo16BitPCM(view, 44, samples)
|
52 |
-
} else {
|
53 |
-
writeFloat32(view, 44, samples)
|
54 |
-
}
|
55 |
-
return buffer
|
56 |
-
}
|
57 |
-
|
58 |
-
function interleave(inputL, inputR) {
|
59 |
-
var length = inputL.length + inputR.length
|
60 |
-
var result = new Float32Array(length)
|
61 |
-
var index = 0
|
62 |
-
var inputIndex = 0
|
63 |
-
while (index < length) {
|
64 |
-
result[index++] = inputL[inputIndex]
|
65 |
-
result[index++] = inputR[inputIndex]
|
66 |
-
inputIndex++
|
67 |
-
}
|
68 |
-
return result
|
69 |
-
}
|
70 |
-
|
71 |
-
function writeFloat32(output, offset, input) {
|
72 |
-
for (var i = 0; i < input.length; i++, offset += 4) {
|
73 |
-
output.setFloat32(offset, input[i], true)
|
74 |
-
}
|
75 |
-
}
|
76 |
-
|
77 |
-
function floatTo16BitPCM(output, offset, input) {
|
78 |
-
for (var i = 0; i < input.length; i++, offset += 2) {
|
79 |
-
var s = Math.max(-1, Math.min(1, input[i]))
|
80 |
-
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true)
|
81 |
-
}
|
82 |
-
}
|
83 |
-
|
84 |
-
function writeString(view, offset, string) {
|
85 |
-
for (var i = 0; i < string.length; i++) {
|
86 |
-
view.setUint8(offset + i, string.charCodeAt(i))
|
87 |
-
}
|
88 |
-
}
|
89 |
-
|
90 |
-
// Safari does not support promise-based decodeAudioData, need to use callback instead.
|
91 |
-
const decodeAudioData = buffer => new Promise((res, rej) => {
|
92 |
-
new AudioContext().decodeAudioData(buffer, res, rej)
|
93 |
-
})
|
94 |
-
const startRecording = async () => {
|
95 |
-
const data = []
|
96 |
-
// Ask for mic permissions.
|
97 |
-
const stream = await navigator.mediaDevices.getUserMedia({ video: false, audio: true })
|
98 |
-
window.stream = stream
|
99 |
-
// Use polyfill for older browsers.
|
100 |
-
if (!window.MediaRecorder) {
|
101 |
-
window.MediaRecorder = OpusMediaRecorder
|
102 |
-
window.recorder = new MediaRecorder(stream, {}, {
|
103 |
-
OggOpusEncoderWasmPath: 'https://cdn.jsdelivr.net/npm/opus-media-recorder@latest/OggOpusEncoder.wasm',
|
104 |
-
WebMOpusEncoderWasmPath: 'https://cdn.jsdelivr.net/npm/opus-media-recorder@latest/WebMOpusEncoder.wasm'
|
105 |
-
})
|
106 |
-
}
|
107 |
-
else window.recorder = new MediaRecorder(stream)
|
108 |
-
// Handle incoming data.
|
109 |
-
window.recorder.ondataavailable = e => data.push(e.data)
|
110 |
-
window.recorder.start()
|
111 |
-
window.recorder.onerror = e => { throw e.error || new Error(e.name) }
|
112 |
-
window.recorder.onstop = async (e) => {
|
113 |
-
const blob = new Blob(data)
|
114 |
-
const fetchedBlob = await fetch(URL.createObjectURL(blob))
|
115 |
-
const arrayBuffer = await fetchedBlob.arrayBuffer()
|
116 |
-
// Convert to wav format.
|
117 |
-
const wav = audioBufferToWav(await decodeAudioData(arrayBuffer))
|
118 |
-
const formData = new FormData()
|
119 |
-
formData.append('files', new Blob([wav], { type: 'audio/wave' }), 'sound.wav')
|
120 |
-
// Send the audio file to Wave server.
|
121 |
-
const res = await fetch(wave.uploadURL, { method: 'POST', body: formData })
|
122 |
-
const { files } = await res.json()
|
123 |
-
// Emit event (q.events.audio.captured) with a URL of the audio file at Wave server.
|
124 |
-
window.wave.emit('audio', 'captured', files[0])
|
125 |
-
}
|
126 |
-
}
|
127 |
-
const stopRecording = () => {
|
128 |
-
window.recorder.stop()
|
129 |
-
window.stream.getTracks().forEach(track => track.stop())
|
130 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|