(function (window) {
|
//兼容
|
window.URL = window.URL || window.webkitURL;
|
//请求麦克风
|
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
|
|
var Recorder = function (stream, wsock) {
|
//创建一个音频环境对象
|
audioContext = window.AudioContext || window.webkitAudioContext;
|
var context = new audioContext();
|
|
config = {};
|
config.channelCount = 1;
|
config.numberOfInputChannels = config.channelCount;
|
config.numberOfOutputChannels = config.channelCount;
|
config.sampleBits = config.sampleBits || 16; //采样数位 8, 16
|
//config.sampleRate = config.sampleRate || (context.sampleRate / 6); //采样率(1/6 44100)
|
config.sampleRate = config.sampleRate || 8000; //采样率16K
|
//创建缓存,用来缓存声音
|
config.bufferSize = 8192;
|
var ws = wsock;
|
//将声音输入这个对像
|
var audioInput = context.createMediaStreamSource(stream);
|
|
//设置音量节点
|
var volume = context.createGain();
|
audioInput.connect(volume);
|
|
// 创建声音的缓存节点,createScriptProcessor方法的
|
// 第二个和第三个参数指的是输入和输出都是声道数。
|
var recorder = context.createScriptProcessor(config.bufferSize, config.channelCount, config.channelCount);
|
|
//用来储存读出的麦克风数据,和压缩这些数据,将这些数据转换为WAV文件的格式
|
var audioData = {
|
size: 0 //录音文件长度
|
, buffer: [] //录音缓存
|
, inputSampleRate: context.sampleRate //输入采样率
|
, inputSampleBits: 16 //输入采样数位 8, 16
|
, outputSampleRate: config.sampleRate //输出采样率
|
, oututSampleBits: config.sampleBits //输出采样数位 8, 16
|
, input: function (data) {
|
this.buffer = [];
|
this.size = 0;
|
this.buffer.push(new Float32Array(data)); //Float32Array
|
this.size += data.length;
|
}
|
, getRawData: function () { //合并压缩
|
//合并
|
var data = new Float32Array(this.size);
|
var offset = 0;
|
for (var i = 0; i < this.buffer.length; i++) {
|
data.set(this.buffer[i], offset);
|
offset += this.buffer[i].length;
|
}
|
//压缩
|
var getRawDataion = parseInt(this.inputSampleRate / this.outputSampleRate);
|
var length = data.length / getRawDataion;
|
var result = new Float32Array(length);
|
var index = 0, j = 0;
|
while (index < length) {
|
result[index] = data[j];
|
j += getRawDataion;
|
index++;
|
}
|
return result;
|
}
|
|
,closeContext:function(){
|
context.close(); //关闭AudioContext否则录音多次会报错。
|
}
|
,getPureWavData: function(offset) {
|
var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits)
|
var bytes = this.getRawData();
|
var dataLength = bytes.length * (sampleBits / 8);
|
var buffer = new ArrayBuffer(dataLength);
|
var data = new DataView(buffer);
|
data = this.reshapeWavData(sampleBits, offset, bytes, data);
|
// var wavd = new Int8Array(data.buffer.byteLength);
|
// var pos = 0;
|
// for (var i = 0; i < data.buffer.byteLength; i++, pos++) {
|
// wavd[i] = data.getInt8(pos);
|
// } // return wavd;
|
|
// return new Blob([data], { type: 'audio/wav' });
|
return data;
|
|
}
|
,reshapeWavData: function(sampleBits, offset, iBytes, oData) {
|
if (sampleBits === 8) {
|
for (var i = 0; i < iBytes.length; i++, offset++) {
|
var s = Math.max(-1, Math.min(1, iBytes[i]));
|
var val = s < 0 ? s * 0x8000 : s * 0x7FFF;
|
val = parseInt(255 / (65535 / (val + 32768)));
|
oData.setInt8(offset, val, true);
|
}
|
} else {
|
for (var i = 0; i < iBytes.length; i++, offset += 2) {
|
var s = Math.max(-1, Math.min(1, iBytes[i]));
|
oData.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
|
}
|
}
|
return oData;
|
}
|
|
};
|
|
//开始录音
|
this.start = function () {
|
audioInput.connect(recorder);
|
recorder.connect(context.destination);
|
};
|
|
//停止
|
this.stop = function () {
|
recorder.disconnect();
|
|
audioData.closeContext();
|
}
|
|
|
var $bo=$("#inbo");
|
var $change=$("#change");
|
var width=$bo.width();
|
//音频采集
|
recorder.onaudioprocess = function (e) {
|
audioData.input(e.inputBuffer.getChannelData(0));
|
|
var data = audioData.getPureWavData(0);
|
if (ws != null)
|
ws.send(data);
|
|
|
};
|
|
};
|
//抛出异常
|
Recorder.throwError = function (message) {
|
throw new function () { this.toString = function () { return message; };};
|
};
|
//是否支持录音
|
Recorder.canRecording = (navigator.getUserMedia != null);
|
//获取录音机
|
Recorder.get = function (callback, wsock) {
|
if (callback) {
|
if (navigator.getUserMedia) {
|
navigator.getUserMedia(
|
{ audio: true } //只启用音频 A
|
, function (stream) { //stream这个参数是麦克风的输入流,将这个流传递给Recorder
|
var rec = new Recorder(stream, wsock);
|
callback(rec);
|
}
|
, function (error) {
|
switch (error.code || error.name) {
|
case 'PERMISSION_DENIED':
|
case 'PermissionDeniedError':
|
Recorder.throwError('用户拒绝提供信息。');
|
break;
|
case 'NOT_SUPPORTED_ERROR':
|
case 'NotSupportedError':
|
Recorder.throwError('浏览器不支持硬件设备。');
|
break;
|
case 'MANDATORY_UNSATISFIED_ERROR':
|
case 'MandatoryUnsatisfiedError':
|
Recorder.throwError('无法发现指定的硬件设备。');
|
break;
|
default:
|
Recorder.throwError('无法打开麦克风。异常信息:' + (error.code || error.name));
|
break;
|
}
|
});
|
} else {
|
Recorder.throwErr('当前浏览器不支持录音功能。'); return;
|
}
|
}
|
};
|
window.Recorder = Recorder;
|
|
})(window);
|