近期公司需要一個(gè)頁(yè)面錄音的小功能,因此調(diào)研了Web Audio API,因?yàn)橐恢币矝](méi)怎么做過(guò)js開發(fā)撕彤,期間踩坑無(wú)數(shù)鱼鸠,在此做一記錄,希望能幫到后面有需要的人羹铅。
1.Web Audio介紹
Web Audio API 官方文檔不但提供了在Web上控制音頻的一個(gè)非常有效通用的系統(tǒng)蚀狰,而且提供了大量音頻相關(guān)的基礎(chǔ)知識(shí),對(duì)入門音頻編程有極大的幫助,允許開發(fā)者機(jī)型自選音頻源,對(duì)音頻添加特效职员,使音頻可視化麻蹋,添加空間效果等功能的開發(fā)。
一個(gè)簡(jiǎn)單而典型的Web audio流程如下:
- 1.創(chuàng)建音頻上下文
- 2.在音頻上下文理創(chuàng)建源 例如 振蕩器, 流
- 3.創(chuàng)建效果節(jié)點(diǎn)焊切,例如混響哥蔚、雙二階濾波器、平移蛛蒙、壓縮
- 4.為音頻選擇一個(gè)目的地,例如你的系統(tǒng)揚(yáng)聲器
- 5.連接源到效果器渤愁,對(duì)目的地進(jìn)行效果輸出
詳細(xì)信息請(qǐng)參考官方網(wǎng)站,網(wǎng)站地址:
https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Audio_API
2.JS this指針說(shuō)明
開發(fā)過(guò)程中關(guān)于JavaScript this踩了無(wú)數(shù)坑牵祟。在函數(shù)調(diào)用過(guò)程中,this綁定的對(duì)象各種出錯(cuò)抖格,因此本節(jié)中特意給出JavaScript this 的官方說(shuō)明.
官方參考文檔:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/this
3.錄音源碼Demo
鑒于在開發(fā)過(guò)程中未找到合適的參考代碼诺苹,把源碼公布如下:
/*!
* Record Javascript Library
*/
'use strict';
/**
* AudioRecorder類.
* @constructor
*/
function AudioRecorder(){
//麥克風(fēng)
this.mDevice = null;
//從麥克風(fēng)獲取的音頻流
this.mMediaStream = null;
this.mAudioContext = null;
this.mAudioFromMicrophone = null;
this.mMediaRecorder = null;
this.mStatus = "stop";
this.mChunks = [];
//回調(diào)函數(shù)
this.onStopCallBack = null;
}
AudioRecorder.prototype={
/**
* 獲取錄音機(jī)對(duì)象設(shè)備
* @method getAudioRecorderDevice
* @for AudioRecorder
* @returns {Promise} 返回一個(gè)promise對(duì)象
*/
getAudioRecorderDevice: function(){
//僅用來(lái)進(jìn)行錄音
var constraints = { audio: true};
// 老的瀏覽器可能根本沒(méi)有實(shí)現(xiàn) mediaDevices,所以我們可以先設(shè)置一個(gè)空的對(duì)象
if(navigator.mediaDevices === undefined) {
navigator.mediaDevices = {};
}
// 一些瀏覽器部分支持 mediaDevices雹拄。我們不能直接給對(duì)象設(shè)置 getUserMedia
// 因?yàn)檫@樣可能會(huì)覆蓋已有的屬性收奔。這里我們只會(huì)在沒(méi)有g(shù)etUserMedia屬性的時(shí)候添加它。
if(navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function(constraints) {
// 首先滓玖,如果有g(shù)etUserMedia的話坪哄,就獲得它
var getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
// 一些瀏覽器根本沒(méi)實(shí)現(xiàn)它 - 那么就返回一個(gè)error到promise的reject來(lái)保持一個(gè)統(tǒng)一的接口
if(!getUserMedia) {
return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
}
// 否則,為老的navigator.getUserMedia方法包裹一個(gè)Promise
this.mDevice = new Promise(function(resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject);
});
}
}
else
{
this.mDevice = navigator.mediaDevices.getUserMedia(constraints);
}
if(this.mDevice != null)
{
this.mDevice.then((mediaStream) => { this.openDeviceSuccess.call(this,mediaStream) },this.openDeviceFailure);
}
},
addOnStopCallback : function (onStop)
{
this.onStopCallBack = onStop;
},
openDeviceSuccess : function(mediaStream)
{
this.mMediaStream = mediaStream;
},
openDeviceFailure : (reason) =>
{
let errorMessage;
switch(reason.name) {
// 用戶拒絕
case 'NotAllowedError':
case 'PermissionDeniedError':
errorMessage = '用戶已禁止網(wǎng)頁(yè)調(diào)用錄音設(shè)備';
break;
// 沒(méi)接入錄音設(shè)備
case 'NotFoundError':
case 'DevicesNotFoundError':
errorMessage = '錄音設(shè)備未找到';
break;
// 其它錯(cuò)誤
case 'NotSupportedError':
errorMessage = '不支持錄音功能';
break;
default:
errorMessage = '錄音調(diào)用錯(cuò)誤';
window.console.log(error);
}
alert(errorMessage);
},
/**
* 開始錄音
* @method startRecord
* @for AudioRecorder
* @return {Boolean}
*/
startRecord : function(){
let retValue = false;
if(this.mStatus == "stop")
{
this.mChunks = [];
if(this.mMediaRecorder == null)
{
const AudioContext = window.AudioContext || window.webkitAudioContext;
this.mAudioContext = new AudioContext();
//創(chuàng)建音頻源
this.mAudioFromMicrophone= this.mAudioContext.createMediaStreamSource(this.mMediaStream);
//創(chuàng)建目的節(jié)點(diǎn)
var destination = this.mAudioContext.createMediaStreamDestination();
this.mMediaRecorder = new MediaRecorder(destination.stream);
this.mAudioFromMicrophone.connect(destination);
this.mMediaRecorder.ondataavailable = (audioData) => { this.onProcessData.call(this,audioData)};
this.mMediaRecorder.onstop = (event) => { this.onStop.call(this,event)};
}
this.mMediaRecorder.start();
this.mStatus = "record";
retValue = true;
}
return retValue;
},
onProcessData : function(audioData)
{
this.mChunks.push(audioData.data);
},
onStop : function (event)
{
//var blob = new Blob(this.mChunks, { 'type' : 'audio/ogg; codecs=opus' });
var blob = new Blob(this.mChunks, { 'type' : 'audio/mpeg' });
var mp3URL = URL.createObjectURL(blob);
if(this.onStopCallBack != null)
{
this.onStopCallBack(mp3URL);
}
},
/**
* 結(jié)束錄音
* @method stopRecord
* @for AudioRecorder
*/
stopRecord: function(){
if(this.mStatus == "record")
{
this.mMediaRecorder.requestData();
this.mMediaRecorder.stop();
this.mStatus = "stop";
}
}
}
4.結(jié)束語(yǔ)
這次小功能的調(diào)研势篡,發(fā)現(xiàn)Web Audio API的功能強(qiáng)大翩肌,可以完成各種音頻處理的工作,后續(xù)如果有時(shí)間可以寫一個(gè)頁(yè)面版的音頻應(yīng)用禁悠。
參考:
https://github.com/mdn/webaudio-examples
https://mdn.github.io/webaudio-examples/create-media-stream-destination/