最近的项目又需要用到录音,年前有过调研,再次翻出来使用,这里做一个记录。
html5 提供了录音支持,因此可以方便使用 HTML5 来录音,来实现录音、语音识别等功能,语音开发必备。但是 ES 标准提供的 API 并不人性化,不方便使用,并且不提供保存为 wav 的功能,开发起来费劲啊!!
github 寻找轮子,发现 Recorder.js,基本上可以满足需求了,良好的封装,支持导出 wav,但是存在:
提供创建 recorder 工具函数,封装 audio 接口:
- static createRecorder(callback, config) {
- window.AudioContext = window.AudioContext || window.webkitAudioContext;
- window.URL = window.URL || window.webkitURL;
- navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
- if (navigator.getUserMedia) {
- navigator.getUserMedia({
- audio: true
- } //只启用音频
- ,
- function(stream) {
- var audio_context = new AudioContext;
- var input = audio_context.createMediaStreamSource(stream);
- var rec = new Recorder(input, config);
- callback(rec);
- },
- function(error) {
- switch (error.code || error.name) {
- case 'PERMISSION_DENIED':
- case 'PermissionDeniedError':
- throwError('用户拒绝提供信息。');
- break;
- case 'NOT_SUPPORTED_ERROR':
- case 'NotSupportedError':
- throwError('浏览器不支持硬件设备。');
- break;
- case 'MANDATORY_UNSATISFIED_ERROR':
- case 'MandatoryUnsatisfiedError':
- throwError('无法发现指定的硬件设备。');
- break;
- default:
- throwError('无法打开麦克风。异常信息:' + (error.code || error.name));
- break;
- }
- });
- } else {
- throwError('当前浏览器不支持录音功能。');
- return;
- }
- }
H5 录制的默认是 44k 的,文件大,不方便传输,因此需要进行重新采样,一般采用插值取点方法:
以下代码主要来自 stackoverflow:
- /**
- * 转换采样率
- * @param data
- * @param newSampleRate 目标采样率
- * @param oldSampleRate 原始数据采样率
- * @returns {any[]|Array}
- */
- function interpolateArray(data,newSampleRate,oldSampleRate){
- varfitCount= Math.round(data.length *(newSampleRate / oldSampleRate));
- varnewData= new Array();
- varspringFactor= new Number((data.length - 1) / (fitCount- 1));newData[0]=data[0]; // for new allocation
- for(vari= 1;i<fitCount- 1;i++){
- vartmp=i*springFactor;
- varbefore= new Number(Math.floor(tmp)).toFixed();
- varafter= new Number(Math.ceil(tmp)).toFixed();
- varatPoint=tmp-before;newData[i]= this.linearInterpolate(data[before],data[after],atPoint);
- }newData[fitCount- 1]=data[data.length - 1]; // for new allocation
- returnnewData;
- }
- function linearInterpolate(before,after,atPoint){
- returnbefore+(after-before)*atPoint;
- }
修改导出 wav 函数 exportWAV,增加采样率选项:
- /**
- * 导出wav
- * @param type
- * @param desiredSamplingRate 期望的采样率
- */
- function exportWAV(type, desiredSamplingRate) {
- // 默认为16k
- desiredSamplingRate = desiredSamplingRate || 16000;
- var buffers = [];
- for (var channel = 0; channel < numChannels; channel++) {
- var buffer = mergeBuffers(recBuffers[channel], recLength);
- // 需要转换采样率
- if (desiredSamplingRate != sampleRate) {
- // 插值去点
- buffer = interpolateArray(buffer, desiredSamplingRate, sampleRate);
- }
- buffers.push(buffer);
- }
- var interleaved = numChannels === 2 ? interleave(buffers[0], buffers[1]) : buffers[0];
- var dataview = encodeWAV(interleaved, desiredSamplingRate);
- var audioBlob = new Blob([dataview], {
- type: type
- });
- self.postMessage({
- command: 'exportWAV',
- data: audioBlob
- });
- }
为了方便绘制音量、波形图,需要获取到实时数据:
config 新增一个回调函数 onaudioprocess:
- config = {
- bufferLen: 4096,
- numChannels: 1,
- // 默认单声道
- mimeType: 'audio/wav',
- onaudioprocess: null
- };
修改录音数据处理函数:
- this.node.onaudioprocess =(e)=> {
- if(!this.recording)return;
- varbuffer=[];
- for(varchannel= 0;channel< this.config.numChannels;channel++){
- buffer.push(e.inputBuffer.getChannelData(channel));
- }
- // 发送给worker
- this.worker.postMessage({
- command: 'record',
- buffer:buffer});
- // 数据回调
- if(this.config.onaudioprocess){
- this.config.onaudioprocess(buffer[0]);
- }
- };
这样,在创建 recorder 时,配置 onaudioprocess 就可以获取到实时数据了
编码计算耗时,需要放到 worker 执行:
接口函数新增 encode,发送消息给 worker,让 worker 执行:
- encode(cb, buffer, sampleRate) {
- cb = cb || this.config.callback;
- if (!cb) throw new Error('Callback not set');
- this.callbacks.encode.push(cb);
- this.worker.postMessage({
- command: 'encode',
- buffer: buffer,
- sampleRate: sampleRate
- });
- }
worker 里新增 encode 函数,处理 encode 请求,完成后执行回调
- self.onmessage = function(e) {
- switch (e.data.command) {
- case 'encode':
- encode(e.data.buffer, e.data.sampleRate);
- break;
- }
- };
- encode(cb, buffer, sampleRate) {
- cb = cb || this.config.callback;
- if (!cb) throw new Error('Callback not set');
- this.callbacks.encode.push(cb);
- this.worker.postMessage({
- command: 'encode',
- buffer: buffer,
- sampleRate: sampleRate
- });
- }
增加一个上传函数:
- exportWAVAndUpload(url, callback) {
- var _url = url;
- exportWAV(function(blob) {
- var fd = new FormData();
- fd.append("audioData", blob);
- var xhr = new XMLHttpRequest();
- if (callback) {
- xhr.upload.addEventListener("progress",
- function(e) {
- callback('uploading', e);
- },
- false);
- xhr.addEventListener("load",
- function(e) {
- callback('ok', e);
- },
- false);
- xhr.addEventListener("error",
- function(e) {
- callback('error', e);
- },
- false);
- xhr.addEventListener("abort",
- function(e) {
- callback('cancel', e);
- },
- false);
- }
- xhr.open("POST", url);
- xhr.send(fd);
- })
- }
= 点击下载
今天再次看这个项目,发现这个项目已经不维护了,
Note: This repository is not being actively maintained due to lack of time and interest. If you maintain or know of a good fork, please let me know so I can direct future visitors to it. In the meantime, if this library isn't working, you can find a list of popular forks here: http://forked.yannick.io/mattdiamond/recorderjs.
作者推荐 https://github.com/chris-rudmin/Recorderjs,提供更多的功能:
.
- 4096
- Voice,
- 2048
- Full Band Audio,
- 2049
- Restricted Low Delay. Defaults to
- 2051
.
- 2049
is fastest with lowest complexity.
- 0
is slowest with highest complexity. The encoder selects a default when this is not specified.
- 10
.
- 20
- encoderWorker.min.js
. Supported values are
- 48000
,
- 8000
,
- 12000
,
- 16000
or
- 24000
.
- 48000
recording, so you can re-
- stop
without re-
- start
. Defaults to
- initStream
.
- false
.
- 40
and
- 0
. Defaults to
- 1
- 0
= mono,
- 1
= stereo. Defaults to
- 2
. Maximum
- 1
channels are supported.
- 2
.
- 16000
is fastest with lowest quality.
- 0
is slowest with highest quality. Defaults to
- 10
.
- 3
event will fire after each encoded page. Defaults to
- dataAvailable
.
- false
推荐使用
来源: http://www.cnblogs.com/xiaoqi/p/6993912.html