您的位置:首页 > Web前端 > JavaScript

使用Web Audio API实现基于浏览器的Web端录音

2015-12-31 11:18 706 查看
有时候会有通过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);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  audio javascript