前端实现在浏览器网页中录音

article/2025/7/22 5:53:10


一、整体实现的思想

页面中实现录音需要使用浏览器提供的MediaRecorder API,所以要实现页面录音就需要浏览器支持MediaStream Recording相关的功能,即浏览器能够获取浏览器的录音权限。

页面内容,需要一个记录录音开始和结束的按钮,以及一个用于播放录音的标签<audio>

然后设计具体的实现细节:首先我们进入页面需要判断浏览器是否支持该API,如果支持的话,然后再获取浏览器的录音权限,获取权限之后,我们的页面上分别由录音按钮和播放音频的标签audio,然后我们就需要设计逻辑,在点击录音按钮的时候就开始记录音频(实现这一步的前提是我们需要创建录音实例),然后实现启动录音和结束录音,录音结束之后,我们就把获取的录音实例放在audio中,当我们点击播放的时候就可以实现将录音播放出来。

二、具体细节和函数

浏览器获取录音的权限

/*获取浏览器的录音权限,contraint是需要获取权限的列表*/const constraints = { audio: true };//返回的是Promise对象,因为需要等到用户确定授予权限的时候,我们才会处理下面的步骤,navigator是浏览器对象,我们就是通过navigator获取录音权限,成功回调的话就会获取到一个stream,然后将这个stream放入到我们下面创建的录音实例里面去navigator.mediaDevices.getUserMedia(constraints)

创建录音实例

//通过该方法创建录音实例var mediaRecorder = new MediaRecorder(stream);

启动录音

//通过点击按钮来启动或者结束录音//获取按钮节点const recordBtn = document.querySelector(".record-btn");//创建录音实例const mediaRecorder = new MediaRecorder(stream);
recordBtn.onclick = () => { mediaRecorder.start(); console.log("录音中...");};//

MediaRecorder 实例上有个 state 状态,可用来判断录音器当前的活动状态,总共有三种值:

inactive:处于休息状态,要么是没开始,要么是开始后已经停止。

recording:录音中

paused:已经开始,但被暂停了,不是停止也没有被恢复。

//更加完整的录音逻辑recordBtn.onclick = () => { if (mediaRecorder.state === "recording") {  mediaRecorder.stop();  recordBtn.textContent = "record";  console.log("录音结束"); } else {  mediaRecorder.start();  console.log("录音中...");  recordBtn.textContent = "stop"; } console.log("录音器状态:", mediaRecorder.state);};

音频数据的获取

上面按钮处理来自用户的交互,只负责启动或停止录音。音频的数据还是从 MediaRecorder 实例上通过监听其相应的事件来完成的。

当录音开始时,会触发其 MediaRecorder.ondataavailable 事件,从该事件回调的入参为 BlobEvent,从它身上取到 event.data 便是我们需要的音频数据。因为数据是一段一段产生的,所以需要暂存到一个数组中。

const chunks = [];mediaRecorder.ondataavailable = function(e) { chunks.push(e.data);};

录音的结束和音频的播放,通过监听 MediaRecorder.onstop 事件,将收集好的音频数据创建成Blob 对象,然后 通过 URL.createObjectURL 创建成 html 中 <audio> 标签可使用的资源链接。

mediaRecorder.onstop = e => { var blob = new Blob(chunks, { type: "audio/ogg; codecs=opus" }); chunks = []; var audioURL = window.URL.createObjectURL(blob); audio.src = audioURL;};

三、详细完整代码

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>实现在网页上录音</title></head><body><div class="app"><button class="record-btn">record</button><audio controls class="audio-player" ></audio><button class="record-btn1">record</button><audio controls class="audio-player1" ></audio></div><script>if (navigator.mediaDevices.getUserMedia) {const constraints = { audio: true };navigator.mediaDevices.getUserMedia(constraints).then(stream => {console.log("授权成功!");const recordBtn = document.querySelector(".record-btn");const mediaRecorder = new MediaRecorder(stream);var chunks = [];recordBtn.onclick = () => {if (mediaRecorder.state === "recording") {mediaRecorder.stop();console.log(chunks)mediaRecorder.onstop = e => {var blob= new Blob(chunks, { type: "audio/ogg; codecs=opus" });chunks = [];var audioURL = window.URL.createObjectURL(blob);const audioSrc = document.querySelector(".audio-player");audioSrc.src = audioURL;};recordBtn.textContent = "record";console.log("录音结束");} else {mediaRecorder.start();mediaRecorder.ondataavailable = function(e) {chunks.push(e.data);};console.log(chunks)console.log("录音中...");recordBtn.textContent = "stop";}console.log("录音器状态:", mediaRecorder.state);};
},() => {console.error("授权失败!");});} else {console.error("浏览器不支持 getUserMedia");}if (navigator.mediaDevices.getUserMedia) {const constraints1 = { audio: true };navigator.mediaDevices.getUserMedia(constraints1).then(stream1 => {console.log("授权成功!");const recordBtn1 = document.querySelector(".record-btn1");const mediaRecorder1 = new MediaRecorder(stream1);var chunks1 = [];recordBtn1.onclick = () => {if (mediaRecorder1.state === "recording") {mediaRecorder1.stop();//console.log(chunks)mediaRecorder1.onstop = e => {var blob1 = new Blob(chunks1, { type: "audio/ogg; codecs=opus" });chunks1 = [];var audioURL1 = window.URL.createObjectURL(blob1);const audioSrc1 = document.querySelector(".audio-player1");audioSrc1.src = audioURL1;};recordBtn1.textContent = "record";console.log("录音结束");} else {mediaRecorder1.start();mediaRecorder1.ondataavailable = function(e) {chunks1.push(e.data);};//console.log(chunks)console.log("录音中...");recordBtn1.textContent = "stop";}console.log("录音器状态:", mediaRecorder1.state);};
},() => {console.error("授权失败!");});} else {console.error("浏览器不支持 getUserMedia");}</script></body></html>

(这个代码可以直接复制粘贴使用,查看效果,然后再一步步的学习细节)

MediaRecorder 实例上有个 state 状态,可用来判断录音器当前的活动状态,总共有三种值:

inactive:处于休息状态,要么是没开始,要么是开始后已经停止。

recording:录音中

paused:已经开始,但被暂停了,不是停止也没有被恢复。

本文完~

最后

  • 欢迎加我微信(winty230),拉你进技术群,长期交流学习...

  • 欢迎关注「前端Q」,认真学前端,做个专业的技术人...

点个在看支持我吧


http://chatgpt.dhexx.cn/article/jQWiBsrb.shtml

相关文章

基于神经网络的自适应最优控制

《 N e u r a l n e t w o r k a p p r o a c h t o c o n t i n u o u s − t i m e d i r e c t a d a p t i v e o p t i m a l c o n t r o l f o r p a r t i a l l y u n k n o w n n o n l i n e a r s y s t e m s 》 D r a g u n a V r a b i e ∗ , F r a n k L e w …

堆排序(排升序为啥建大堆,排降序为啥建小堆)

简介&#xff1a; 之前对堆排序认识的不是很透彻&#xff0c;今天回过头来再把堆排序的知识整理整理&#xff01;以及排升序为什么要建大堆&#xff0c;排降序要建小堆。 正文&#xff1a; 首先我们要知道&#xff1a;   ①堆的逻辑是一颗完全二叉树&#xff1b;   ②它使…

残差网络Residual Networks-残差网络的创建、训练、测试、调参

残差网络的创建、训练、测试、调参加粗样式 在Keras中实现残差网络模型的创建&#xff0c;并通过模型来实现对图片的分类。 残差网络的预备知识 网络越深越好&#xff1f; 随着网络层级的不断增加&#xff0c;模型精度不断得到提升&#xff0c;而当网络层级增加到一定的数目…

堆排序,为什么升序排列要建大堆,降序排列要建小堆

堆排序中用到了建立大小堆和向下调整的内容&#xff0c;对这些内容有些不了解的同学可以去补一补专门写堆的博客&#xff0c;方便更好的理解堆排序数据结构之堆&#xff08;Heap&#xff09;&#xff0c;堆的相关操作&#xff0c;用堆模拟优先级队列。 如果把待排序序列分为未排…

操作系统——动态分配算法(首次适应算法,最佳适应算法,最坏适应算法及回收)

操作系统——动态分配 写的时间早了&#xff0c;有些许漏洞和不足&#xff0c;请大家不要介意 分配方式可分为四类:单一连续分配、固定分区分配、动态分区分配以及动态可重定位分区分配算法四种方式&#xff0c;其中动态分区分配算法就是此实验的实验对象。动态分区分配又称为…

pandas中对列进行排序(单列/多列)/(升序/降序)/(多列升序,降序控制)

前言 我想把数据分析刻进DNA里 如下面的数据,对price,要进行最简单的升序操作: 这个好整: import pandas as pdshop pd.read_csv("data/shop.csv", index_colid)shop.sort_values(byprice, inplaceTrue)结果: 如果你想整活(被迫)要把数据进行降序排列: 按照降序,传…

最先适应法、最佳适应法、下次适配法、最差适配法

题&#xff1a; 分析&#xff1a; 1. 首先分析是不是最差适配法&#xff0c;最差适配法意思是首先找到最大的内存空间进行分配&#xff0c; 对于请求的5K存储空间&#xff0c;首先找到地址200K容量为56K的地方进行分配&#xff0c;然后剩余51K。 再对请求的15K进行分配&…

自适应滤波器更新算法-EP1

自适应滤波器更新算法-EP1 自适应滤波器是回声消除系统中非常重要的一个功能模块&#xff0c;而对于自适应滤波器来说&#xff0c;如果更新滤波器系数则是关键所在。本文将介绍几种现有的滤波器更新算法&#xff0c;并附上Matlab测试代码。 1、LMS算法 1.1算法原理 LMS算法即…

自适应神经网络算法原理,单神经元自适应控制

关于神经网络自适应控制求助 这句话你可以直接用&#xff0c;不用加引用。因为这句话是很容易验证的。在网络层数、隐含层节点数逐渐增加&#xff0c;训练次数增加之后&#xff0c;他的拟合能力也是不断增加的&#xff0c;所以说&#xff0c;他可以以任意精度逼近任何非线性连…

【转载】梯度下降算法的参数更新公式

NN这块的公式&#xff0c;前馈网络是矩阵乘法。损失函数的定义也是一定的。 但是如何更新参数看了不少描述&#xff0c;下面的叙述比较易懂的&#xff1a; 1、在吴恩达的CS229的讲义的第四页直接给出参数迭代公式 在UFLDL中反向传导算法一节也是直接给出的公式 2、例子&#x…

Java中Comparator的个人简单理解(升序降序)与使用

目录 Java自定义排序返回值简单记忆理解实践LInkedList升序&#xff08;默认情况&#xff09;降序 PriorityQueue升序下的小顶堆&#xff08;默认情况&#xff09;降序下的大顶堆 总结补充数组类型自定义排序降序排序 数组 Java自定义排序返回值简单记忆理解 默认情况下&#…

深度残差收缩网络(从信号降噪的角度进行理解)

本文探讨了深度残差收缩网络的另一种理解方式。 传统信号降噪算法的常见步骤是&#xff1a; ① 采用某种信号变换方法&#xff08;例如小波、经验模态分解&#xff09;&#xff0c;将含噪信号变换到另外一种形态&#xff08;例如小波系数、本征模态分量等&#xff09;。在这些…

NIPS 2016 深度学习 迁移学习 ---残差转移网络用于无监督领域自适应

深度学习的成功得益于大量的标注数据&#xff0c;而数据标注是非常消耗资源的。当一个问题中缺少标注数据时&#xff0c;可以从另一个源中所学知识迁移过来&#xff0c;并且用于新问题中。 清华大学的学者提出了一种新的方法&#xff08;https://arxiv.org/pdf/1602.04433.pdf&…

深度残差网络+自适应参数化ReLU激活函数(调参记录21)Cifar10~95.12%

本文在调参记录20的基础上,将残差模块的个数,从27个增加到60个,继续测试深度残差网络ResNet+自适应参数化ReLU激活函数在Cifar10数据集上的表现。 自适应参数化ReLU函数被放在了残差模块的第二个卷积层之后,这与Squeeze-and-Excitation Networks或者深度残差收缩网络是相似…

已知两个长度分别为m 和 n 的升序链表,合并降序链表,求时间复杂度

王道数据结构上一道题&#xff1a; 之前我看到一个电子版的书&#xff0c;上边答案解析写的有点错误&#xff0c; 听说有些同学买的实体书上&#xff0c;答案解析也是这样写的 这个是刊印错误&#xff0c;很显然2Max( m , n )大于等于 m n 而且这个解析也不够清晰。 解析&a…

波束形成 常见自适应波束形成算法信(干)噪比增益影响因素

0、其他补充 均匀线阵波束形成器的信噪比增益上确界可由下式表示&#xff1a; 其中为阵元数&#xff0c;所以为了方便起见&#xff0c;一般的稳健自适应波束形成算法在仿真过程中的阵元数量均设置为10。 阵列的导向矢量可由下式表示&#xff1a; 以首个阵元为参考阵元&#xff…

两个升序链表合并成一个降序链表的时间复杂度

王道考研P7 第六题 【2013年统考真题】已知两个长度分别为m和n的升序链表&#xff0c;若将它们合并为长度为mn的一个降序链表&#xff0c;则最坏情况下的时间复杂度是&#xff08;&#xff09; A. O(n) B. O(mn) C. O(min(m,n)) D. O(max(m,n)) 答案是D 注意&#xff0c;此题…

无线传感器网络路由优化中的能量均衡LEACH改进算法

文章目录 一、理论基础1、LEACH算法概述2、改进的LEACH算法 二、算法流程图三、仿真实验与分析四、参考文献 一、理论基础 1、LEACH算法概述 请参考这里。 2、改进的LEACH算法 改进的LEACH算法&#xff08;LEACH-N&#xff09;主要针对LEACH算法分簇阶段的缺陷而改进的&…

机器学习之自适应增强(Adaboost)

1.Adaboost简介 **Adaptive boosting(自适应增强)是一种迭代算法&#xff0c;其核心思想是针对同一个训练集训练不同的弱分类器&#xff0c;然后把这些弱分类器集合起来&#xff0c;构成一个强分类器&#xff0c;Adaboost可处理分类和回归问题。了解Adaboost算法之前&#xff…

自适应阈值(adaptiveThreshold)分割原理及实现

背景介绍及原理 前面介绍了OTSU算法和最大熵算法&#xff0c;但这两种算法都属于全局阈值法&#xff0c;所以对于某些光照不均的图像&#xff0c;这种全局阈值分割的方法会显得苍白无力&#xff0c;如下图&#xff1a; 显然&#xff0c;这样的阈值处理结果不是我们想要的&…