先记录一下查过的有用的文章
更改AudioContext的采样率
Web AudioContext 录音如何降低采样率呢?
浅谈H5音频处理(更多谈谈录音方向的内容)
音视频录制浅解
字符串arraybuffer转换及拼接
JS ArrayBuffer 类型转换及拼接
1.调用AudioContext
的接口操作音频PCM数据
缓冲区
和采样率
的关系:缓冲区应该大于数据读取间隔*采样率。
navigator.mediaDevices.getUserMedia({audio: true}, initRecorder)
function initRecorder(stream) {
const AudioContext = window.AudioContext
const audioContext = new AudioContext()
// 创建MediaStreamAudioSourceNode对象,给定媒体流(例如,来自navigator.getUserMedia实例),然后可以播放和操作音频。
const audioInput = audioContext.createMediaStreamSource(stream)
// 缓冲区大小为4096,控制着多长时间需触发一次audioprocess事件
const bufferSize = 4096
// 创建一个javascriptNode,用于使用js直接操作音频数据
// 第一个参数表示每一帧缓存的数据大小,可以是256, 512, 1024, 2048, 4096, 8192, 16384,值越小一帧的数据就越小,声音就越短,onaudioprocess 触发就越频繁。4096的数据大小大概是0.085s,就是说每过0.085s就触发一次onaudioprocess,第二,三个参数表示输入帧,和输出帧的通道数。这里表示2通道的输入和输出,当然我也可以采集1,4,5等通道
const recorder = audioContext.createScriptProcessor(bufferSize, 1, 1)
// 每个满足一个分片的buffer大小就会触发这个回调函数
recorder.onaudioprocess = recorderProcess
// const monitorGainNode = audioContext.createGain()
// 延迟0.01秒输出到扬声器
// monitorGainNode.gain.setTargetAtTime(音量, audioContext.currentTime, 0.01)
// monitorGainNode.connect(audioContext.destination)
// audioInput.connect(monitorGainNode)
// const recordingGainNode = audioContext.createGain()
// recordingGainNode.gain.setTargetAtTime(音量, audioContext.currentTime, 0.01)
// recordingGainNode.connect(audioContext.scriptProcessorNode)
// 将音频的数据流输出到这个jsNode对象中
audioInput.connect(recorder)
// 最后先音频流输出到扬声器。(将录音流原本的输出位置再定回原来的目标地)
recorder.connect(audioContext.destination)
}
// 可以取到对应声道的数据,进行分别处理
function recorderProcess(e) {
// 左声道
const left = e.inputBuffer.getChannelData(0);
}
2.数据处理与转换(涉及到采样率sampleRate
和位深度bitDepth
)
获取麦克风采样率
const AudioContext = window.AudioContext
const audioContext = new AudioContext()
// 可读属性
console.log(audioContext.sampleRate)
// 44100
修改采样率
function interleave(e){
var t = e.length;
sampleRate += 0.0;
outputSampleRate += 0.0;
var s = 0,
o = sampleRate / outputSampleRate,
u = Math.ceil(t * outputSampleRate / sampleRate),
a = new Float32Array(u);
for (i = 0; i < u; i++) {
a[i] = e[Math.floor(s)];
s += o;
}
return a;
}
剩下还有两个问题。将音频流转为16位深,这里就比较简单了。只要确保你生成的位数足够就行。比如,8位深的音频只需要生成一个Uint8Array
,16位深就要生成2个的长度。new Unit8Array(bitDepth / 8)
3.位深相关知识点
WAV格式的音频文件,如果是没有经过压缩的,那里面的PCM数据,就是最原始的采样数据。根据采样位数的不同,常见的有8bit PCM数据, 16bit PCM数据, 由于种种原因,有时可能会遇到8bit 与16bit PCM编码之间相互转换的情况,
首先明确一点的时,8b PCM的采样数据是无符号数据, 而16b PCM的采样数据是有符号数据,因此8bit PCM转换为 16bit PCM时,必须先把无符号数据转换为有符号数。
4.获取当前电脑可使用的音视频设备
// 获取录音设备列表
if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
navigator.mediaDevices.enumerateDevices()
.then((devices) => {
console.log('录音设备列表',devices)
// audioinput
let arr = []
devices.forEach((item) => {
if (item.kind === 'audioinput') {
arr.push({
label: item.label,
id: item.deviceId
})
}
})
console.log(arr)
})
.catch(() => {
alert('列表获取失败')
})
}
5.字符串和音频流拼接
const pcm_prefix = Buffer.from('----SPEECH_AUDIO', 'utf-8').toString('latin1')
const pcmString = String.fromCharCode
.apply(null, new Unit8Array(event.data))
.aplit(pcm_prefix)[2]
if (pcmString) {
const arraybuffer = Buffer.from(pcmString, 'latin1')
const dataAudio =. new Unit8Array(arraybuffer) // 最终音频
}