chai / assets /js /hooks /microphone.js
jonatanklosko's picture
Add chat
0fea377
raw
history blame contribute delete
2.92 kB
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;