详解HTML5录音碰到的坑-
说实话,一开端都没接触过 HTML5 的 Audio API,并且要基于在我们接手前的代码中进行优化。固然其中也踩了不少坑,这次也会环绕这几个坑来说说感想(会省去一些根本对象的初始化和猎取,由于这些内容不是这次的重点,有乐趣的同窗可以自行查寻 MDN 上的文档):
调取 Audio API 的兼容性写法
猎取录音声音的大小(应当是频率)
暂停录音的兼容性写法
猎取目前录音工夫
录音前的预备
开端录音前,要先猎取目前设施可否支撑 Audio API。早期的办法 navigator.getUserMedia 已经被 navigator.mediaDevices.getUserMedia 所取代。正常来说此刻大局部的现代阅读器都已经支撑 navigator.mediaDevices.getUserMedia 的用途了,固然 MDN 上也给出了兼容性的写法
const promisifiedOldGUM = function(constraints) { // First get ahold of getUserMedia, if present const getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; // Some browsers just don't implement it - return a rejected promise with an error // to keep a consistent interface if (!getUserMedia) { return Promise.reject( new Error('getUserMedia is not implemented in this browser') ); } // Otherwise, wrap the call to the old navigator.getUserMedia with a Promise return new Promise(function(resolve, reject) { getUserMedia.call(navigator, constraints, resolve, reject); }); }; // Older browsers might not implement mediaDevices at all, so we set an empty object first if (navigator.mediaDevices === undefined) { navigator.mediaDevices = {}; } // Some browsers partially implement mediaDevices. We can't just assign an object // with getUserMedia as it would overwrite existing properties. // Here, we will just add the getUserMedia property if it's missing. if (navigator.mediaDevices.getUserMedia === undefined) { navigator.mediaDevices.getUserMedia = promisifiedOldGUM; }
由于这个办法是异步的,所以我们可以对没法兼容的设施进行友爱的提醒
navigator.mediaDevices.getUserMedia(constraints).then( function(mediaStream) { // 成功 }, function(error) { // 失败 const { name } = error; let errorMessage; switch (name) { // 会员拒绝 case 'NotAllowedError': case 'PermissionDeniedError': errorMessage = '会员已制止网页调取录音设施'; break; // 没接入录音设施 case 'NotFoundError': case 'DevicesNotFoundError': errorMessage = '录音设施未寻到'; break; // 其它差错 case 'NotSupportedError': errorMessage = '不支撑录音功能'; break; default: errorMessage = '录腔调用差错'; window.console.log(error); } return errorMessage; } );
一切顺利的话,我们就可以进入下一步了。
(这里有对猎取高低文的办法进行了省去,由于这不是这次的重点)
开端录音、暂停录音
这里有个比拼特殊的点,就是需要增加一个中间变量来标识可否目前可否在录音。由于在火狐阅读器上,我们发明一个题目,录音的流程都是正常的,但是点击暂停时却发明怎么也暂停不了,我们当时是运用 disconnect 办法。这种方式是不过关的,这种办法是需要断开所有的连贯才可以。后来发明,应当添加一个中间变量 this.isRecording 来推断目前可否正在录音,当点击开端时,将其设定为 true ,暂停时将其设定为 false 。
当我们开端录音时,会有一个录音监听的事件 onaudioprocess ,要是返回 true 则会将流写入,要是返回 false 则不会将其写入。因而推断 this.isRecording ,要是为 false 则直接 return
// 一些初始化 const audioContext = new AudioContext(); const sourceNode = audioContext.createMediaStreamSource(mediaStream); const scriptNode = audioContext.createScriptProcessor( BUFFER_SIZE, INPUT_CHANNELS_NUM, OUPUT_CHANNELS_NUM ); sourceNode.connect(this.scriptNode); scriptNode.connect(this.audioContext.destination); // 监听录音的历程 scriptNode.onaudioprocess = event => { if (!this.isRecording) return; // 推断可否正则录音 this.buffers.push(event.inputBuffer.getChannelData(0)); // 猎取目前频道的数据,并写入数组 };
固然这里也会有个坑,就是没法再运用,自带猎取目前录音时长的办法了,由于现实上并不是真正的暂停,而是没有将流写入而已。于是我们还需要猎取一下目前录音的时长,需要通过一个公式进行猎取
const getDuration = () => { return (4096 * this.buffers.length) / this.audioContext.sampleRate // 4096为一个流的长度,sampleRate 为采样率 }
这样就能够猎取准确的录音时长了。
完毕录音
完毕录音的方式,我采纳的是先暂停,之后需要试听或者其它的操纵先施行,然后再将存储流的数组长度置为 0。
猎取频率
getVoiceSize = analyser => { const dataArray = new Uint8Array(analyser.frequencyBinCount); analyser.getByteFrequencyData(dataArray); const data = dataArray.slice(100, 1000); const sum = data.reduce((a, b) => a + b); return sum; };
其它
HTTPS:在 chrome 下需要全站有 HTTPS 才允许运用
微信:在微信内置的阅读器需要调取 JSSDK 才干运用
音频格局转换:音频格局的方式也有许多了,能查到的大局部材料,大家根本上是互相 copy,固然还有一个音频质量的题目,这里就不赘述了。
结语
这次碰到的大局部题目都是兼容性的题目,因而在上面踩了不少坑,尤为是挪移端的题目,一开端还有涌现由于猎取录音时长写法差错的题目,致使直接卡死的状况。这次的阅历也填补了 HTML5 API 上的一些空白,固然最重要的还是要提示一下大家,这种原生的 API 文档还是直接查看 MDN 来的简略粗犷!