有时候会有通过Web端录音的需求,那么如何实现呢,通过Web Audio API能够实现,具体可以查看官网的API,下面是实现单声道录音(压缩音频大小)的一个例子,共需要两个js文件,具体如下:
第一个js文件: recorder.js
(function(window){
var WORKER_PATH = 'resources/js/recorderWorker.js'; //自己设置recorderWorker.js的路径
var Recorder = function(source, cfg){
var config = cfg || {};
var bufferLen = config.bufferLen || 4096;
this.context = source.context;
this.node = this.context.createScriptProcessor(bufferLen, 1, 1);
var worker = new Worker(config.workerPath || WORKER_PATH);
worker.postMessage({
command: 'init',
config: {
sampleRate: this.context.sampleRate //默认的是48000
//sampleRate:11025
}
});
var recording = false,
currCallback;
this.node.onaudioprocess = function(e){
if (!recording) return;
worker.postMessage({
command: 'record',
buffer: [
e.inputBuffer.getChannelData(0)
//e.inputBuffer.getChannelData(1)
]
});
}
this.configure = function(cfg){
for (var prop in cfg){
if (cfg.hasOwnProperty(prop)){
config[prop] = cfg[prop];
}
}
}
this.record = function(){
recording = true;
}
this.stop = function(){
recording = false;
}
this.clear = function(){
worker.postMessage({ command: 'clear' });
}
this.getBuffer = function(cb) {
currCallback = cb || config.callback;
worker.postMessage({ command: 'getBuffer' })
}
this.exportWAV = function(cb, type){
currCallback = cb || config.callback;
type = type || config.type || 'audio/wav';
if (!currCallback) throw new Error('Callback not set');
worker.postMessage({
command: 'exportWAV',
type: type
});
}
worker.onmessage = function(e){
var blob = e.data;
currCallback(blob);
}
source.connect(this.node);
this.node.connect(this.context.destination); //this should not be necessary
};
Recorder.forceDownload = function(blob, filename){
var url = (window.URL || window.webkitURL).createObjectURL(blob);
var link = window.document.createElement('a');
link.href = url;
link.download = filename || 'output.wav';
var click = document.createEvent("Event");
click.initEvent("click", true, true);
link.dispatchEvent(click);
}
window.Recorder = Recorder;
})(window);
第二个js文件: recorderWorker.js
var recLength = 0,
recBuffersL = [],
recBuffersR = [],
sampleRate;
this.onmessage = function(e){
switch(e.data.command){
case 'init':
init(e.data.config);
break;
case 'record':
record(e.data.buffer);
break;
case 'exportWAV':
exportWAV(e.data.type);
break;
case 'getBuffer':
getBuffer();
break;
case 'clear':
clear();
break;
}
};
function init(config){
sampleRate = config.sampleRate;
}
function record(inputBuffer){
recBuffersL.push(inputBuffer[0]);
//recBuffersR.push(inputBuffer[1]);
recLength += inputBuffer[0].length;
}
function exportWAV(type){
var bufferL = mergeBuffers(recBuffersL, recLength);
var interleaved = interleave(bufferL);
var dataview = encodeWAV(interleaved);
var audioBlob = new Blob([dataview], { type: type });
this.postMessage(audioBlob);
}
function getBuffer() {
var buffers = [];
buffers.push( mergeBuffers(recBuffersL, recLength) );
this.postMessage(buffers);
}
function clear(){
recLength = 0;
recBuffersL = [];
recBuffersR = [];
}
function mergeBuffers(recBuffers, recLength){
var result = new Float32Array(recLength);
var offset = 0;
for (var i = 0; i < recBuffers.length; i++){
result.set(recBuffers[i], offset);
offset += recBuffers[i].length;
}
return result;
}
function interleave(inputL){
//修改采样率时 , 要做如下修改
var compression = 44100 / 11025; //计算压缩率
var length = inputL.length / compression;
var result = new Float32Array(length);
var index = 0,
inputIndex = 0;
while (index < length) {
result[index] = inputL[inputIndex];
inputIndex += compression;//每次都跳过3个数据
index++;
}
return result;
}
function floatTo16BitPCM(output, offset, input){
for (var i = 0; i < input.length; i++, offset+=2){
var s = Math.max(-1, Math.min(1, input[i]));
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
}
}
function floatTo8BitPCM(output, offset, input) {
for (var i = 0; i < input.length; i++, offset++) { //这里只能加1了
var s = Math.max(-1, Math.min(1, input[i]));
var val = s < 0 ? s * 0x8000 : s * 0x7FFF;
val = parseInt(255 / (65535 / (val + 32768))); //这里有一个转换的代码,这个是我个人猜测的,就是按比例转换
output.setInt8(offset, val, true);
}
}
function writeString(view, offset, string){
for (var i = 0; i < string.length; i++){
view.setUint8(offset + i, string.charCodeAt(i));
}
}
function encodeWAV(samples){
//修改采样率 时 , 要添加混合声道时的处理
var dataLength = samples.length;
var buffer = new ArrayBuffer(44 + dataLength);
var view = new DataView(buffer);
var sampleRateTmp = 11205 ;//sampleRate;//写入新的采样率
var sampleBits = 8;
var channelCount = 1;
var offset = 0;
/* 资源交换文件标识符 */
writeString(view, offset, 'RIFF'); offset += 4;
/* 下个地址开始到文件尾总字节数,即文件大小-8 */
view.setUint32(offset, /*32*/ 36 + dataLength, true); offset += 4;
/* WAV文件标志 */
writeString(view, offset, 'WAVE'); offset += 4;
/* 波形格式标志 */
writeString(view, offset, 'fmt '); offset += 4;
/* 过滤字节,一般为 0x10 = 16 */
view.setUint32(offset, 16, true); offset += 4;
/* 格式类别 (PCM形式采样数据) */
view.setUint16(offset, 1, true); offset += 2;
/* 通道数 */
view.setUint16(offset, channelCount, true); offset += 2;
/* 采样率,每秒样本数,表示每个通道的播放速度 */
view.setUint32(offset, sampleRateTmp, true); offset += 4;
/* 波形数据传输率 (每秒平均字节数) 通道数×每秒数据位数×每样本数据位/8 */
view.setUint32(offset, sampleRateTmp * channelCount * (sampleBits / 8), true); offset +=4;
/* 快数据调整数 采样一次占用字节数 通道数×每样本的数据位数/8 */
view.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2;
/* 每样本数据位数 */
view.setUint16(offset, sampleBits, true); offset += 2;
/* 数据标识符 */
writeString(view, offset, 'data'); offset += 4;
/* 采样数据总数,即数据总大小-44 */
view.setUint32(offset, dataLength, true); offset += 4;
/* 采样数据 */
floatTo8BitPCM(view, 44, samples);
return view;
}
使用方法:
1.使能HTML5录音功能
window.URL = window.URL;
navigator.getUserMedia = navigator.webkitGetUserMedia;
if (navigator.getUserMedia) {
navigator.getUserMedia({video:true,audio:true}, function(localMediaStream){
var context = new AudioContext();
var mediaStreamSource = context.createMediaStreamSource(localMediaStream);
window.MediaStreamSource = mediaStreamSource;
}, function(){
});
}else {
Ext.Msg.alert(“消息提示”,“你的浏览器不支持录音”);
}
2.开始录音
var rec = new Recorder(MediaStreamSource);
window.rec=rec;
3.停止录音
window.rec.stop();
rec.exportWAV(function(blob) {
//导出录音过程产生的blob数据,传给后台实现音频的保存
});
console.log(blob);