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; | |