Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
6720716a5This file is pinned to the selected release. Download the original to open it in your design tool.
import AudioIn from 'embedded:io/audio/in'
export default class Microphone {
recording: boolean
#audioIn: AudioIn | null
onReadable?: (this: AudioIn, byteLength: number, sampleCount?: number) => void
constructor() {
this.recording = false
this.#audioIn = null
}
start() {
if (this.recording) {
throw new Error('already recording')
}
const self = this
this.#audioIn = new AudioIn({
onReadable(size, sampleCount) {
if (self.onReadable) {
self.onReadable.call(this, size, sampleCount)
}
},
})
this.#audioIn.start()
this.recording = true
}
stop() {
this.#audioIn?.close()
this.#audioIn = null
this.recording = false
}
async record(durationMilliSec = 3000): Promise<ArrayBuffer> {
if (this.recording) {
throw new Error('already recording')
}
this.recording = true
const HEADER_SIZE = 44
return new Promise((resolve) => {
let writeOffset = 0
const audioin = new AudioIn({
onReadable(size) {
const remaining = dataView.byteLength - writeOffset
trace(`${remaining}\n`)
const chunkSize = Math.min(size, remaining)
const chunk = this.read(chunkSize)
if (!chunk) {
this.close()
resolve(wavBuffer)
} else {
dataView.set(new Uint8Array(chunk), writeOffset)
writeOffset += chunkSize
if (writeOffset >= dataView.byteLength) {
this.close()
resolve(wavBuffer)
}
}
},
})
// generate header
const { sampleRate, channels, bitsPerSample } = audioin
const byteRate = sampleRate * channels * (bitsPerSample >> 3)
const contentLength = (durationMilliSec / 1000) * byteRate
const wavBuffer = new ArrayBuffer(HEADER_SIZE + contentLength)
const headerView = new DataView(wavBuffer)
const dataView = new Uint8Array(wavBuffer, HEADER_SIZE)
headerView.setUint8(0, 'R'.charCodeAt(0))
headerView.setUint8(1, 'I'.charCodeAt(0))
headerView.setUint8(2, 'F'.charCodeAt(0))
headerView.setUint8(3, 'F'.charCodeAt(0))
headerView.setUint32(4, 36 + contentLength, true)
headerView.setUint8(8, 'W'.charCodeAt(0))
headerView.setUint8(9, 'A'.charCodeAt(0))
headerView.setUint8(10, 'V'.charCodeAt(0))
headerView.setUint8(11, 'E'.charCodeAt(0))
headerView.setUint8(12, 'f'.charCodeAt(0))
headerView.setUint8(13, 'm'.charCodeAt(0))
headerView.setUint8(14, 't'.charCodeAt(0))
headerView.setUint8(15, ' '.charCodeAt(0))
headerView.setUint32(16, 16, true)
headerView.setUint16(20, 1, true) // AudioFormat = 1 (PCM)
headerView.setUint16(22, channels, true)
headerView.setUint32(24, sampleRate, true)
headerView.setUint32(28, byteRate, true)
headerView.setUint16(32, (channels * bitsPerSample) >> 3, true)
headerView.setUint16(34, bitsPerSample, true)
headerView.setUint8(36, 'd'.charCodeAt(0))
headerView.setUint8(37, 'a'.charCodeAt(0))
headerView.setUint8(38, 't'.charCodeAt(0))
headerView.setUint8(39, 'a'.charCodeAt(0))
headerView.setUint32(40, contentLength, true)
// start recording
audioin.start()
})
}
}