We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
前段时间,项目中用到html5音频采集,主要是和微信录音一样,流程是按住说话,右侧滑动可以音频转文字,左侧滑动撤销。关于按住说话及左右侧滑动交互,相对简单,主要是运用了onTouchStart,onTouchMove,onTouchEnd三个事件完成。我之前文章也有过模仿微信语音播放效果动画,其中左右侧滑动位置主要是根据 e.targetTouches[0].pageX,和 e.targetTouches[0].pageY来完成的。本文着重讲解一下html5音频采集解决方案。
之所以叫解决方案,因为这里有涉及了音频采集的各种问题,例如音波,格式转换,录制格式,多段音频拼接,多个语音合成等等。语音采集我主要用了。
音频采集可以利用navigator.mediaDevices.getUserMedia({ audio: true }),来自己手动采集。 判断有无权限及开启权限可以如下代码
export const checkIsOpenPermission = () => { if (navigator.mediaDevices === undefined) { navigator.mediaDevices = {} } // 一些浏览器部分支持 mediaDevices。我们不能直接给对象设置 getUserMedia // 因为这样可能会覆盖已有的属性。这里我们只会在没有getUserMedia属性的时候添加它。 if (navigator.mediaDevices.getUserMedia === undefined) { let getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia navigator.mediaDevices.getUserMedia = function (constraints) { // 首先,如果有getUserMedia的话,就获得它 // 一些浏览器根本没实现它 - 那么就返回一个error到promise的reject来保持一个统一的接口 if (!getUserMedia) { return Promise.reject(new Error('getUserMedia is not implemented in this browser')) } // 否则,为老的navigator.getUserMedia方法包裹一个Promise return new Promise(function (resolve, reject) { getUserMedia.call(navigator, constraints, resolve, reject) }) } } navigator.mediaDevices .getUserMedia({ audio: true }) .then(function (stream) { // 用户成功开启 console.log('user allow audio') }) .catch(function (error) { switch (error.code || error.name) { case 'PERMISSION_DENIED': case 'PermissionDeniedError': Toast.show({ style: { zIndex: 1000000 }, content: '用户拒绝提供信息' }) break case 'NOT_SUPPORTED_ERROR': case 'NotSupportedError': Toast.show({ style: { zIndex: 1000000 }, content: '浏览器不支持硬件设备。' }) break case 'MANDATORY_UNSATISFIED_ERROR': case 'MandatoryUnsatisfiedError': Toast.show({ style: { zIndex: 1000000 }, content: '无法发现指定的硬件设备。' }) break default: Toast.show({ style: { zIndex: 1000000 }, content: '无法打开麦克风' }) break } }) }
// 拼接音频的方法 const concatAudio = (arrBufferList) => { // 获得 AudioBuffer const audioBufferList = arrBufferList; // 最大通道数 const maxChannelNumber = Math.max(...audioBufferList.map(audioBuffer => audioBuffer.numberOfChannels)); // 总长度 const totalLength = audioBufferList.map((buffer) => buffer.length).reduce((lenA, lenB) => lenA + lenB, 0); // 创建一个新的 AudioBuffer const newAudioBuffer = audioContext.createBuffer(maxChannelNumber, totalLength, audioBufferList[0].sampleRate); // 将所有的 AudioBuffer 的数据拷贝到新的 AudioBuffer 中 let offset = 0; audioBufferList.forEach((audioBuffer, index) => { for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) { newAudioBuffer.getChannelData(channel).set(audioBuffer.getChannelData(channel), offset); } offset += audioBuffer.length; }); return newAudioBuffer; }
// 获取音频AudioBuffer
// AudioContext const audioContext = new AudioContext(); // 基于src地址获得 AudioBuffer 的方法 const getAudioBuffer = (src) => { return new Promise((resolve, reject) => { fetch(src).then(response => response.arrayBuffer()).then(arrayBuffer => { audioContext.decodeAudioData(arrayBuffer).then(buffer => { resolve(buffer); }); }) }) }
// 合并音频的方法 const mergeAudio = (arrBufferList) => { // 获得 AudioBuffer const audioBufferList = arrBufferList; // 最大播放时长 const maxDuration = Math.max(...audioBufferList.map(audioBuffer => audioBuffer.duration)); // 最大通道数 const maxChannelNumber = Math.max(...audioBufferList.map(audioBuffer => audioBuffer.numberOfChannels)); // 创建一个新的 AudioBuffer const newAudioBuffer = audioContext.createBuffer(maxChannelNumber, audioBufferList[0].sampleRate * maxDuration, audioBufferList[0].sampleRate); // 将所有的 AudioBuffer 的数据合并到新的 AudioBuffer 中 audioBufferList.forEach((audioBuffer, index) => { for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) { const outputData = newAudioBuffer.getChannelData(channel); const bufferData = audioBuffer.getChannelData(channel); for (let i = audioBuffer.getChannelData(channel).length - 1; i >= 0; i--) { outputData[i] += bufferData[i]; } newAudioBuffer.getChannelData(channel).set(outputData); } }); return newAudioBuffer; }
// 定义一个AudioContext对象 // 因为 Web Audio API都是源自此对象 const audioContext = new AudioContext(); // 创建一个gainNode对象 // gainNode可以对音频输出进行一些控制 const gainNode = audioContext.createGain(); // 音量设置为20% gainNode.gain.value = 0.2; // 这个很有必要,建立联系 gainNode.connect(audioContext.destination); // 创建AudioBufferSourceNode let source = audioContext.createBufferSource(); // 获取音频资源 fetch('./bgmusic.mp3') .then(res => res.arrayBuffer()) .then(buffer => audioContext.decodeAudioData(buffer)) .then(audioBuffer => { source.buffer = audioBuffer; source.connect(gainNode); }); // 当需要播放的时候,执行 source.start(0);
我这边推荐的html5音频处理开源库是https://gitee.com/xiangyuecn/Recorder#%E9%99%84android-hybrid-app---webview%E4%B8%AD%E5%BD%95%E9%9F%B3%E7%A4%BA%E4%BE%8B
一、引入方式
//必须引入的核心,换成require也是一样的。注意:recorder-core会自动往window下挂载名称为Recorder对象,全局可调用window.Recorder,也许可自行调整相关源码清除全局污染 import Recorder from 'recorder-core' //引入相应格式支持文件;如果需要多个格式支持,把这些格式的编码引擎js文件放到后面统统引入进来即可 import 'recorder-core/src/engine/mp3' import 'recorder-core/src/engine/mp3-engine' //如果此格式有额外的编码引擎(*-engine.js)的话,必须要加上 //以上三个也可以合并使用压缩好的recorder.xxx.min.js //比如 import Recorder from 'recorder-core/recorder.mp3.min' //已包含recorder-core和mp3格式支持 //可选的插件支持项 import 'recorder-core/src/extensions/waveview' //ts import 提示:npm包内已自带了.d.ts声明文件(不过是any类型)
recorder-core库,可以录制各种格式视频,支持格式转换,假如语音转文字,需要pcm格式,这个开源库可以直接录制pcm格式,支持多个pcm格式的拼接。
const pcmMerge = function (fileBytesList, bitRate, sampleRate, True, False) { //计算所有文件总长度 var size = 0 for (var i = 0; i < fileBytesList.length; i++) { size += fileBytesList[i].byteLength } //全部直接拼接到一起 var fileBytes = new Uint8Array(size) var pos = 0 for (var i = 0; i < fileBytesList.length; i++) { var bytes = fileBytesList[i] fileBytes.set(bytes, pos) pos += bytes.byteLength } //计算合并后的总时长 var duration = Math.round(((size * 8) / bitRate / sampleRate) * 1000) True(fileBytes, duration, { bitRate: bitRate, sampleRate: sampleRate }, size) }
二、支持波形播放插件
例如WaveView插件,FrequencyHistogramView插件等等,音频录制波形可视化插件
三、支持音频混音、变速变调音频转换
简单的音频录制需求可以自己实现,但是需要复杂的,音频解决方案,我这边推荐使用recorder-core库,这个库例子比较多,使用相对简单。推荐给大家!
The text was updated successfully, but these errors were encountered:
No branches or pull requests
前言
前段时间,项目中用到html5音频采集,主要是和微信录音一样,流程是按住说话,右侧滑动可以音频转文字,左侧滑动撤销。关于按住说话及左右侧滑动交互,相对简单,主要是运用了onTouchStart,onTouchMove,onTouchEnd三个事件完成。我之前文章也有过模仿微信语音播放效果动画,其中左右侧滑动位置主要是根据 e.targetTouches[0].pageX,和 e.targetTouches[0].pageY来完成的。本文着重讲解一下html5音频采集解决方案。
之所以叫解决方案,因为这里有涉及了音频采集的各种问题,例如音波,格式转换,录制格式,多段音频拼接,多个语音合成等等。语音采集我主要用了。
关于音频采集
音频采集可以利用navigator.mediaDevices.getUserMedia({ audio: true }),来自己手动采集。
判断有无权限及开启权限可以如下代码
多个音频的拼接
// 获取音频AudioBuffer
多个音频的合并
音量的改变
第三方音频解决方案
我这边推荐的html5音频处理开源库是https://gitee.com/xiangyuecn/Recorder#%E9%99%84android-hybrid-app---webview%E4%B8%AD%E5%BD%95%E9%9F%B3%E7%A4%BA%E4%BE%8B
一、引入方式
recorder-core库,可以录制各种格式视频,支持格式转换,假如语音转文字,需要pcm格式,这个开源库可以直接录制pcm格式,支持多个pcm格式的拼接。
二、支持波形播放插件
例如WaveView插件,FrequencyHistogramView插件等等,音频录制波形可视化插件
三、支持音频混音、变速变调音频转换
小结
简单的音频录制需求可以自己实现,但是需要复杂的,音频解决方案,我这边推荐使用recorder-core库,这个库例子比较多,使用相对简单。推荐给大家!
The text was updated successfully, but these errors were encountered: