2019-09-17 11:27:51 4570瀏覽
文章之前想問一下小伙伴們,有多少小伙伴接觸過HTML5的Audio API,并對代碼中進(jìn)行優(yōu)化?若開發(fā)一個有聲課件,大致就是通過導(dǎo)入文檔、圖片等資源后,頁面變?yōu)轭愃芇PT的布局,然后選中一張圖片,可以插入音頻,有單頁編輯和全局編輯兩種模式。其中音頻的導(dǎo)入方式有兩種,一種是從資源庫中導(dǎo)入,還有一種就是要提到的錄音。這其中有不少的坑,本篇文章扣丁學(xué)堂HTML5培訓(xùn)小編就圍繞這幾個坑來說說感受。
錄音前的準(zhǔn)備
開始錄音前,要先獲取當(dāng)前設(shè)備是否支持 Audio API。早期的方法 navigator.getUserMedia 已經(jīng)被 navigator.mediaDevices.getUserMedia 所代替。正常來說現(xiàn)在大部分的現(xiàn)代瀏覽器都已經(jīng)支持 navigator.mediaDevices.getUserMedia 的用法了,當(dāng)然 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; }
因為這個方法是異步的,所以我們可以對無法兼容的設(shè)備進(jìn)行友好的提示
navigator.mediaDevices.getUserMedia(constraints).then( function(mediaStream) { // 成功 }, function(error) { // 失敗 const { name } = error; let errorMessage; switch (name) { // 用戶拒絕 case 'NotAllowedError': case 'PermissionDeniedError': errorMessage = '用戶已禁止網(wǎng)頁調(diào)用錄音設(shè)備'; break; // 沒接入錄音設(shè)備 case 'NotFoundError': case 'DevicesNotFoundError': errorMessage = '錄音設(shè)備未找到'; break; // 其它錯誤 case 'NotSupportedError': errorMessage = '不支持錄音功能'; break; default: errorMessage = '錄音調(diào)用錯誤'; window.console.log(error); } return errorMessage; } );
一切順利的話,我們就可以進(jìn)入下一步了。
(這里有對獲取上下文的方法進(jìn)行了省略,因為這不是這次的重點)
開始錄音、暫停錄音
這里有個比較特別的點,就是需要添加一個中間變量來標(biāo)識是否當(dāng)前是否在錄音。因為在火狐瀏覽器上,我們發(fā)現(xiàn)一個問題,錄音的流程都是正常的,但是點擊暫停時卻發(fā)現(xiàn)怎么也暫停不了,我們當(dāng)時是使用 disconnect 方法。這種方式是不行的,這種方法是需要斷開所有的連接才可以。后來發(fā)現(xiàn),應(yīng)該增加一個中間變量 this.isRecording 來判斷當(dāng)前是否正在錄音,當(dāng)點擊開始時,將其設(shè)置為 true ,暫停時將其設(shè)置為 false 。
當(dāng)我們開始錄音時,會有一個錄音監(jiān)聽的事件 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); // 監(jiān)聽錄音的過程 scriptNode.onaudioprocess = event => { if (!this.isRecording) return; // 判斷是否正則錄音 this.buffers.push(event.inputBuffer.getChannelData(0)); // 獲取當(dāng)前頻道的數(shù)據(jù),并寫入數(shù)組 };
當(dāng)然這里也會有個坑,就是無法再使用,自帶獲取當(dāng)前錄音時長的方法了,因為實際上并不是真正的暫停,而是沒有將流寫入罷了。于是我們還需要獲取一下當(dāng)前錄音的時長,需要通過一個公式進(jìn)行獲取。
const getDuration = () => { return (4096 * this.buffers.length) / this.audioContext.sampleRate // 4096為一個流的長度,sampleRate 為采樣率 }
這樣就能夠獲取正確的錄音時長了。
結(jié)束錄音
結(jié)束錄音的方式,我們采用的是先暫停,之后需要試聽或者其它的操作先執(zhí)行,然后再將存儲流的數(shù)組長度置為 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 才允許使用
微信:在微信內(nèi)置的瀏覽器需要調(diào)用 JSSDK 才能使用
音頻格式轉(zhuǎn)換:音頻格式的方式也有很多了,能查到的大部分資料,大家基本上是互相 copy,當(dāng)然還有一個音頻質(zhì)量的問題,這里就不贅述了。
結(jié)語
這次遇到的大部分問題都是兼容性的問題,因此在上面踩了不少坑,尤其是移動端的問題,一開始還有出現(xiàn)因為獲取錄音時長寫法錯誤的問題,導(dǎo)致直接卡死的情況。這次的經(jīng)歷也彌補(bǔ)了 HTML5 API 上的一些空白,當(dāng)然最重要的還是要提醒一下大家,這種原生的 API 文檔還是直接查看 MDN 來的簡單粗暴!
想要了解更多內(nèi)容的小伙伴可以登錄扣丁學(xué)堂官網(wǎng)咨詢。想要學(xué)好HTML5開發(fā)小編給大家推薦口碑良好的扣丁學(xué)堂,扣丁學(xué)堂有專業(yè)老師制定的HTML5學(xué)習(xí)路線圖輔助學(xué)員學(xué)習(xí),此外還有與時俱進(jìn)的HTML5課程體系和HTML5視頻教程供大家學(xué)習(xí),想要學(xué)好HTML5開發(fā)技術(shù)的小伙伴快快行動吧??鄱W(xué)堂H5技術(shù)交流群:673883249。
【關(guān)注微信公眾號獲取更多學(xué)習(xí)資料】 【掃碼進(jìn)入HTML5前端開發(fā)VIP免費公開課】
查看更多關(guān)于“HTML5開發(fā)技術(shù)資訊”的相關(guān)文章>>