112 lines
3.0 KiB
JavaScript
112 lines
3.0 KiB
JavaScript
let recLength = 0
|
|
let recBuffersL = []
|
|
let recBuffersR = []
|
|
let sampleRate
|
|
|
|
this.onmessage = e => {
|
|
if (e.data.command === 'init') init(e.data.config)
|
|
if (e.data.command === 'record') record(e.data.buffer)
|
|
if (e.data.command === 'exportWAV') exportWAV(e.data.type)
|
|
if (e.data.command === 'getBuffers') getBuffers()
|
|
if (e.data.command === 'clear') clear()
|
|
}
|
|
|
|
const mergeBuffers = (recBuffers, recLength) => {
|
|
let result = new Float32Array(recLength)
|
|
let offset = 0
|
|
for (let i = 0; i < recBuffers.length; i++) {
|
|
result.set(recBuffers[i], offset)
|
|
offset += recBuffers[i].length
|
|
}
|
|
return result
|
|
}
|
|
|
|
const init = config => { sampleRate = config.sampleRate }
|
|
|
|
const record = inputBuffer => {
|
|
recBuffersL.push(inputBuffer[0])
|
|
recBuffersR.push(inputBuffer[1])
|
|
recLength += inputBuffer[0].length
|
|
}
|
|
|
|
const exportWAV = type => {
|
|
const bufferL = mergeBuffers(recBuffersL, recLength)
|
|
const bufferR = mergeBuffers(recBuffersR, recLength)
|
|
const interleaved = interleave(bufferL, bufferR)
|
|
const dataview = encodeWAV(interleaved)
|
|
const audioBlob = new Blob([dataview], {type})
|
|
this.postMessage(audioBlob)
|
|
}
|
|
|
|
const getBuffers = () => {
|
|
let buffers = []
|
|
buffers.push(mergeBuffers(recBuffersL, recLength))
|
|
buffers.push(mergeBuffers(recBuffersR, recLength))
|
|
this.postMessage(buffers)
|
|
}
|
|
|
|
const clear = () => {
|
|
recLength = 0
|
|
recBuffersL = []
|
|
recBuffersR = []
|
|
}
|
|
|
|
const interleave = (inputL, inputR) => {
|
|
const length = inputL.length + inputR.length
|
|
let result = new Float32Array(length)
|
|
let index = 0
|
|
let inputIndex = 0
|
|
while (index < length) {
|
|
result[index++] = inputL[inputIndex]
|
|
result[index++] = inputR[inputIndex]
|
|
inputIndex++
|
|
}
|
|
return result
|
|
}
|
|
|
|
const floatTo16BitPCM = (output, offset, input) => {
|
|
for (let i = 0; i < input.length; i++, offset += 2) {
|
|
let s = Math.max(-1, Math.min(1, input[i]))
|
|
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true)
|
|
}
|
|
}
|
|
|
|
const writeString = (view, offset, string) => {
|
|
for (let i = 0; i < string.length; i++) {
|
|
view.setUint8(offset + i, string.charCodeAt(i))
|
|
}
|
|
}
|
|
|
|
const encodeWAV = samples => {
|
|
let buffer = new ArrayBuffer(44 + samples.length * 2)
|
|
let view = new DataView(buffer)
|
|
/* RIFF identifier */
|
|
writeString(view, 0, 'RIFF')
|
|
/* file length */
|
|
view.setUint32(4, 32 + samples.length * 2, true)
|
|
/* RIFF type */
|
|
writeString(view, 8, 'WAVE')
|
|
/* format chunk identifier */
|
|
writeString(view, 12, 'fmt ')
|
|
/* format chunk length */
|
|
view.setUint32(16, 16, true)
|
|
/* sample format (raw) */
|
|
view.setUint16(20, 1, true)
|
|
/* channel count */
|
|
view.setUint16(22, 2, true)
|
|
/* sample rate */
|
|
view.setUint32(24, sampleRate, true)
|
|
/* byte rate (sample rate * block align) */
|
|
view.setUint32(28, sampleRate * 4, true)
|
|
/* block align (channel count * bytes per sample) */
|
|
view.setUint16(32, 4, true)
|
|
/* bits per sample */
|
|
view.setUint16(34, 16, true)
|
|
/* data chunk identifier */
|
|
writeString(view, 36, 'data')
|
|
/* data chunk length */
|
|
view.setUint32(40, samples.length * 2, true)
|
|
floatTo16BitPCM(view, 44, samples)
|
|
return view
|
|
}
|