web如何实现录制音频,满满干货(上篇)

您所在的位置:网站首页 如何录音系统声音 web如何实现录制音频,满满干货(上篇)

web如何实现录制音频,满满干货(上篇)

2024-07-10 18:46| 来源: 网络整理| 查看: 265

最近的需求,要求web端实现录制音频,包括开始录制,重录,暂停录制,继续录制,结束录制;当然除了录制,还要解决播放问题,开始播放,暂停播放,继续播放,结束播放;然后还要能够上传文件到腾讯云或阿里云;最后还需要有下载操作,下载格式选择;

接下来就一一探讨一下,如何实现这些功能吧~

先说明,我知道能够直接在GitHub上找已经实现的demo和成熟的SDK,但是这里想一起看看如何用JavaScript实现。

获取录制权限

录制之前,肯定要检查一下电脑上是否有麦克风权限,是否已经授权,没有授权,或者没有麦克风设备,是不能进行录制的。

就是会要下面这样一个网页提示

安卓移动端的提示

Ipad的提示

获取麦克风设备,可以看下面

Navigator.mediaDevices 只读属性返回一个 MediaDevices 对象,该对象可提供对相机和麦克风等媒体输入设备以及屏幕共享的连接访问

https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator/mediaDevices

然后它的原型链上面,是有一个getUserMedia来获取媒体

https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getUserMedia

它返回一个 Promise 对象,成功后会resolve回调一个 MediaStream 对象。若用户拒绝了使用权限,或者需要的媒体源不可用,promise会reject回调一个 PermissionDeniedError 或者 NotFoundError。

image.png

能看到,这样就获取到我电脑上连接的麦克风一个设备了,全部的代码如下:

// getUserMedia 版本兼容 static initUserMedia() { if (navigator.mediaDevices === undefined) { navigator.mediaDevices = {}; } if (navigator.mediaDevices.getUserMedia === undefined) { navigator.mediaDevices.getUserMedia = function (constraints) { let getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; if (!getUserMedia) { return Promise.reject(new Error('浏览器不支持 getUserMedia !')); } return new Promise((resolve, reject) => { getUserMedia.call(navigator, constraints, resolve, reject); }); } } } static getPermission() { this.initUserMedia(); return navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => { if (stream) { stream.getTracks().forEach(track => track.stop()); } }); }

调用就直接使用getPermission方法就可以了

window.onload = () => { Recorder.getPermission().then(() => { console.log('给权限了'); }, (error) => { console.log(`${error.name} : ${error.message}`); }); }

如果没有找到声音输入设备,直接调用是会报错的

NotFoundError : Requested device not found

录制 初始化录音实例

音频上下文控制它包含的节点的创建和音频处理或解码的执行。

this.context = new (window.AudioContext || window.webkitAudioContext)();

https://developer.mozilla.org/zh-CN/docs/Web/API/AudioContext

可以使用这个对象来控制音频的处理,从文档中,我们可以知道,它继承自BaseAudioCentext,那么就可以使用父类的方法。

全部动态分贝

动态分贝的获取,就需要使用父类的createAnalyser方法,具体介绍,可以看MDN文档:

https://developer.mozilla.org/zh-CN/docs/Web/API/BaseAudioContext/createAnalyser

createAnalyser() 它可以用来暴露音频时间和频率数据,以及创建数据可视,这就是获取动态分贝,然后进行绘制。

在后面绘制demo的提供了

const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); const analyser = audioCtx.createAnalyser(); // 录音分析节点 // … analyser.fftSize = 2048; // 表示存储频域的大小 const bufferLength = analyser.frequencyBinCount; const dataArray = new Uint8Array(bufferLength); analyser.getByteTimeDomainData(dataArray); // 绘制当前音频源的波形图 function draw() { drawVisual = requestAnimationFrame(draw); analyser.getByteTimeDomainData(dataArray); canvasCtx.fillStyle = "rgb(200, 200, 200)"; canvasCtx.fillRect(0, 0, WIDTH, HEIGHT); canvasCtx.lineWidth = 2; canvasCtx.strokeStyle = "rgb(0, 0, 0)"; canvasCtx.beginPath(); const sliceWidth = (WIDTH * 1.0) / bufferLength; let x = 0; for (let i = 0; i { // audioInput表示音频源节点 // stream是通过navigator.getUserMedia获取的外部(如麦克风)stream音频输出,对于这就是输入 this.audioInput = this.context.createMediaStreamSource(stream); this.stream = stream; } ).then(() => { // audioInput 为声音源,连接到处理节点 recorder this.audioInput.connect(this.analyser); this.analyser.connect(this.recorder); // this.audioInput.connect(this.recorder); // 处理节点 recorder 连接到扬声器 this.recorder.connect(this.context.destination); }); } 录制中

接着,我们监听ScriptProcessorNode的onaudioprocess事件就可以了,这个事件就算录制中的事件

// 音频采集,为节点提供一个处理音频事件的函数 this.recorder.onaudioprocess = e => { // 左声道数据 // getChannelData返回Float32Array类型的pcm数据 let lData = e.inputBuffer.getChannelData(0), rData = null, vol = 0; // 音量百分比 this.lBuffer.push(new Float32Array(lData)); this.size += lData.length; // 判断是否有右声道数据 if (this.config.numChannels === 2) { rData = e.inputBuffer.getChannelData(1); this.rBuffer.push(new Float32Array(rData)); this.size += rData.length; } // 计算录音大小 this.fileSize = Math.floor(this.size / Math.max( this.inputSampleRate / this.outputSampleRate, 1)) * (this.oututSampleBits / 8) // 计算音量百分比 vol = Math.max.apply(Math, lData) * 100; // 统计录音时长 this.duration += 4096 / this.inputSampleRate; // 录音时长回调 this.onprocess && this.onprocess(this.duration); // 录音时长及响度回调 this.onprogress && this.onprogress({ duration: this.duration, fileSize: this.fileSize, vol, }); }

直接输出e.inputBuffer.getChannelData(0),控制台是这样的,那说明这是一个Float32Array数据

Float32Array介绍

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Float32Array

这就是浮点数组,长度就是上面默认的4096

这样的数据也被成为PCM数据格式

下面这篇知乎文章介绍了PCM数据格式,不清楚的可以看看

https://zhuanlan.zhihu.com/p/396273481

采样率(Sample rate)每秒钟采样次数,单位 Hz. 根据场景的不同,采样率也会有所不同,采样率越高,那么采样的声音就更加的接近原始声音,声音的还原度就越高,质量越好,同时占用的空间也会越大。例如,通话时的采样率为 8000Hz,常用的媒体采样率为 44100Hz.

我这台电脑的采样率是48000赫兹

采样位数是16

inputSampleRate = 48000 // 输入采样率 outputSampleRate = 48000 inputSampleBits = 16 // 输入采样位数 outputSampleBits = 16

当前的音量就取,一个Float32Array中的最大值

// 计算音量百分比 vol = Math.max.apply(Math, lData) * 100;

录音的文件大小

this.size = 左声道 + 右声道 // 计算录音大小 this.fileSize = Math.floor(this.size / Math.max( this.inputSampleRate / this.outputSampleRate, 1)) * (this.outputSampleBits / 8)

录制的时长

// 统计录音时长,单位秒 this.duration += 4096 / this.inputSampleRate; 暂停录制

由于开始录制就绑定了录制中的事件,其实暂停录制就是把,输入音频数据全部抛弃掉,所以一个变量控制是否需要暂停录制就可以了

/** * 暂停录音 * * @memberof Recorder */ pauseRecord() { this.needRecord = false; } 恢复录制 /** * 继续录音 * * @memberof Recorder */ resumeRecord() { this.needRecord = true; } 结束录制

结束录制,由于前面开始录制了,连接了媒体

所以结束的时候,就需要断开媒体连接

录制事件停止

动态分贝获取停止

/** * 停止录音 * */ stopRecord() { if (this.audioInput) { this.audioInput.disconnect(); } this.recorder.disconnect(); this.analyser.disconnect(); this.needRecord = true; }

录制的流程图如下:

无标题-2023-12-12-2040.png

由于篇幅太长,所以录制在这篇里面,如何实现录制的音频播放、下载和上传,在下一篇。

未完待续····



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3