Spaces:
Sleeping
Sleeping
const SAMPLING_RATE = 16_000; | |
const Microphone = { | |
mounted() { | |
this.mediaRecorder = null; | |
this.el.addEventListener("mousedown", (event) => { | |
this.startRecording(); | |
}); | |
this.el.addEventListener("mouseup", (event) => { | |
this.stopRecording(); | |
}); | |
}, | |
startRecording() { | |
this.audioChunks = []; | |
navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => { | |
this.mediaRecorder = new MediaRecorder(stream); | |
this.mediaRecorder.addEventListener("dataavailable", (event) => { | |
if (event.data.size > 0) { | |
this.audioChunks.push(event.data); | |
} | |
}); | |
this.mediaRecorder.start(); | |
}); | |
}, | |
stopRecording() { | |
this.mediaRecorder.addEventListener("stop", (event) => { | |
if (this.audioChunks.length === 0) return; | |
const audioBlob = new Blob(this.audioChunks); | |
audioBlob.arrayBuffer().then((buffer) => { | |
const context = new AudioContext({ sampleRate: SAMPLING_RATE }); | |
context.decodeAudioData(buffer, (audioBuffer) => { | |
const pcmBuffer = audioBufferToPcm(audioBuffer); | |
const buffer = convertEndianness32( | |
pcmBuffer, | |
getEndianness(), | |
this.el.dataset.endianness | |
); | |
this.upload("audio", [new Blob([buffer])]); | |
}); | |
}); | |
}); | |
this.mediaRecorder.stop(); | |
}, | |
isRecording() { | |
return this.mediaRecorder && this.mediaRecorder.state === "recording"; | |
}, | |
}; | |
function audioBufferToPcm(audioBuffer) { | |
const numChannels = audioBuffer.numberOfChannels; | |
const length = audioBuffer.length; | |
const size = Float32Array.BYTES_PER_ELEMENT * length; | |
const buffer = new ArrayBuffer(size); | |
const pcmArray = new Float32Array(buffer); | |
const channelDataBuffers = Array.from({ length: numChannels }, (x, channel) => | |
audioBuffer.getChannelData(channel) | |
); | |
// Average all channels upfront, so the PCM is always mono | |
for (let i = 0; i < pcmArray.length; i++) { | |
let sum = 0; | |
for (let channel = 0; channel < numChannels; channel++) { | |
sum += channelDataBuffers[channel][i]; | |
} | |
pcmArray[i] = sum / numChannels; | |
} | |
return buffer; | |
} | |
function convertEndianness32(buffer, from, to) { | |
if (from === to) { | |
return buffer; | |
} | |
// If the endianness differs, we swap bytes accordingly | |
for (let i = 0; i < buffer.byteLength / 4; i++) { | |
const b1 = buffer[i]; | |
const b2 = buffer[i + 1]; | |
const b3 = buffer[i + 2]; | |
const b4 = buffer[i + 3]; | |
buffer[i] = b4; | |
buffer[i + 1] = b3; | |
buffer[i + 2] = b2; | |
buffer[i + 3] = b1; | |
} | |
return buffer; | |
} | |
function getEndianness() { | |
const buffer = new ArrayBuffer(2); | |
const int16Array = new Uint16Array(buffer); | |
const int8Array = new Uint8Array(buffer); | |
int16Array[0] = 1; | |
if (int8Array[0] === 1) { | |
return "little"; | |
} else { | |
return "big"; | |
} | |
} | |
export default Microphone; | |