最近的项目又需要用到录音,年前有过调研,再次翻出来使用,这里做一个记录。

HTML5提供了录音支持,因此可以方便使用HTML5来录音,来实现录音、语音识别等功能,语音开发必备。但是ES标准提供的API并不人性化,不方便使用,并且不提供保存为wav的功能,开发起来费劲啊!!

github寻找轮子,发现Recorder.js,基本上可以满足需求了,良好的封装,支持导出wav,但是存在:

  • wav采样率不可调整
  • recorder创建麻烦,需要自己初始化getUserMedia
  • 无实时数据回调,不方便绘制波形
  • 。。。

改造轮子

创建recorder工具方法

提供创建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) {
var fitCount = Math.round(data.length * (newSampleRate / oldSampleRate));
var newData = new Array();
var springFactor = new Number((data.length - 1) / (fitCount - 1));
newData[0] = data[0]; // for new allocation
for (var i = 1; i < fitCount - 1; i++) {
var tmp = i * springFactor;
var before = new Number(Math.floor(tmp)).toFixed();
var after = new Number(Math.ceil(tmp)).toFixed();
var atPoint = tmp - before;
newData[i] = this.linearInterpolate(data[before], data[after], atPoint);
}
newData[fitCount - 1] = data[data.length - 1]; // for new allocation
return newData;
} function linearInterpolate(before, after, atPoint) {
return before + (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;
var buffer = []; for (var channel = 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});
}

wav上传

增加一个上传函数:

     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,提供更多的功能:

  • bitRate (optional) Specifies the target bitrate in bits/sec. The encoder selects an application-specific default when this is not specified.
  • bufferLength - (optional) The length of the buffer that the internal JavaScriptNode uses to capture the audio. Can be tweaked if experiencing performance issues. Defaults to 4096.
  • encoderApplication - (optional) Specifies the encoder application. Supported values are 2048 - Voice, 2049 - Full Band Audio, 2051 - Restricted Low Delay. Defaults to 2049.
  • encoderComplexity - (optional) Value between 0 and 10 which determines latency and processing for resampling. 0 is fastest with lowest complexity. 10 is slowest with highest complexity. The encoder selects a default when this is not specified.
  • encoderFrameSize (optional) Specifies the frame size in ms used for encoding. Defaults to 20.
  • encoderPath - (optional) Path to encoderWorker.min.js worker script. Defaults to encoderWorker.min.js
  • encoderSampleRate - (optional) Specifies the sample rate to encode at. Defaults to 48000. Supported values are 8000, 12000, 16000, 24000 or 48000.
  • leaveStreamOpen - (optional) Keep the stream around when trying to stop recording, so you can re-start without re-initStream. Defaults to false.
  • maxBuffersPerPage - (optional) Specifies the maximum number of buffers to use before generating an Ogg page. This can be used to lower the streaming latency. The lower the value the more overhead the ogg stream will incur. Defaults to 40.
  • monitorGain - (optional) Sets the gain of the monitoring output. Gain is an a-weighted value between 0 and 1. Defaults to 0
  • numberOfChannels - (optional) The number of channels to record. 1 = mono, 2 = stereo. Defaults to 1. Maximum 2 channels are supported.
  • originalSampleRateOverride - (optional) Override the ogg opus 'input sample rate' field. Google Speech API requires this field to be 16000.
  • resampleQuality - (optional) Value between 0 and 10 which determines latency and processing for resampling. 0 is fastest with lowest quality. 10 is slowest with highest quality. Defaults to 3.
  • streamPages - (optional) dataAvailable event will fire after each encoded page. Defaults to false.

推荐使用


作者:Jadepeng

出处:jqpeng的技术记事本--http://www.cnblogs.com/xiaoqi

您的支持是对博主最大的鼓励,感谢您的认真阅读。

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

HTML5录音控件的更多相关文章

  1. 使用jQuery和CSS自定义HTML5 Video 控件 简单适用

    Html5 Video是现在html5最流行的功能之一,得到了大多数最新版本的浏览器支持.包括IE9,也是如此.不同的浏览器提供了不同的原生态浏览器视频空间.我们制作自定义视频控件为了在所有的浏览器中 ...

  2. 一个不错的Html5 DatePicker控件,主要支持手机端。

    原地址不知道在哪了,下载地址 QQ群:616945527 Html5控件wen文件夹中DatePicker.rar

  3. C# winform 支持html5的 控件

    OpenWebKitSharp WebKit.net c#winform中使用WebKit传递js对象实现与网页交互 分类: .NET开发2013-08-18 23:55 2496人阅读 评论(1)  ...

  4. HTML5 UI 控件Mobiscroll的使用(年月日三级联动)

    概述: 遇到制作一个html5界面,需要选择年月日,其实这个功能很常用.一般我们都是网上找,之前也没有收藏一个自己常用的,今天发现一个不错的库.特此记录一下使用过程,以便以后遇到了方面查阅. 1.官方 ...

  5. HTML5:控件自动获得焦点

    在HTML5中,页面打开后,需要指定的控件自动获得焦点很简单,只需要一个属性就可以实现 -  autofocus. 示例如下: <input type="text" auto ...

  6. HTML5 input控件 placeholder属性

    placeholder 属性提供可描述输入字段预期值的提示信息(hint),该提示会在输入字段为空时显示,并会在字段获得焦点时消失. <input placeholder="请输入姓名 ...

  7. 画板社交工具开发分享——HTML5 canvas控件、PHP、社交分享学习(一)

    不谈国内社交网站(人人.微博等)目前的盈利状况如何,facebook.twitter都已经取得了很大的成功.这一定程度上都得益于人们对社交的喜爱和投入. 目前的情况来看,也有很多人已经对直接的文字分享 ...

  8. 【WEB自动化测试之控件定位】基于HTML5控件的唯一控件属性定位

      一.WEB控件定位是什么 要想弄懂这个问题,我们还是基于实践来学习.我们先来看一条入门级别自动化测试用例的构成. DemoCase:正确用户名和密码登录博客园,登录成功 URL: https:// ...

  9. jquery和css自定义video播放控件

    下面介绍一下通过jquery和css自定义video播放控件. Html5 Video是现在html5最流行的功能之一,得到了大多数最新版本的浏览器支持.包括IE9,也是如此.不同的浏览器提供了不同的 ...

随机推荐

  1. webmagic源码学习(一)

    最近工作主要是一些爬虫相关的东西,由于公司需要构建自己的爬虫框架,在调研过程中参考了许多优秀的开源作品,包括webmagic,webcollector,Spiderman等,通过学习这些优秀的源码获益 ...

  2. C# 读取Execl和Access数据库

    第一次写,请大家指教!!话不多说 直接走代码! /// <summary> /// 打开文件 /// </summary> /// <param name="s ...

  3. nginx与apache配合反向代理技术2

    注意,上次我们只是简单的在同一台服务器模拟搭建了一个新的http服务器(启用了8080端口),使用的是apache,从而模拟了多台服务器实现的Nginx反向代理,通过Nginx向上游代理服务器发送请求 ...

  4. 图像转置的SSE优化(支持8位、24位、32位),提速4-6倍。

    一.前言 转置操作在很多算法上都有着广泛的应用,在数学上矩阵转置更有着特殊的意义.而在图像处理上,如果说图像数据本身的转置,除了显示外,本身并无特殊含义,但是在某些情况下,确能有效的提高算法效率,比如 ...

  5. time元素定义的格式

    time元素表示的是24小时中的某一个时刻或日期,表示时间时允许时差. time元素定义的格式如下: <time datetime="2016-6-15">2016年6 ...

  6. 生产环境中使用Docker Swarm的一些建议

    译者按: 实践中会发现,生产环境中使用单个Docker节点是远远不够的,搭建Docker集群势在必行.然而,面对Kubernetes, Mesos以及Swarm等众多容器集群系统,我们该如何选择呢?它 ...

  7. 【挖洞经验】如何在一条UPDATE查询中实现SQL注入

    直奔主题 跟往常一样,在喝完我最爱的果汁饮料之后,我会习惯性地登录我的Synack账号,然后选择一个应用来进行渗透测试,此时我的“黑客之夜”便正式开始了. 我与很多其他的安全研究人员的习惯一样,我会在 ...

  8. anjular中Service、Factory、Provider的使用与js中创建对象的总结

    在学习anjular中Service的使用时,发现和js中的创建对象的方式有一定的联系,所以总结了anjular中Service.Factory.Provider的使用方式与js创建对象的方式 一.先 ...

  9. InputStream类详解

    InputStream这个抽象类是所有基于字节的输入流的超类,抽象了Java的字节输入模型.在这个类中定义了一些基本的方法.看一下类的定义: public abstract class InputSt ...

  10. Python装饰器实现几类验证功能做法

    最近新需求来了,要给系统增加几个资源权限.尽量减少代码的改动和程序的复杂程度.所以还是使用装饰器比较科学 之前用了一些登录验证的现成装饰器模块.然后仿写一些用户管理部分的权限装饰器.比如下面这种 de ...