Vue3实时检测的录音功能

如果有人声并且大于20db,则开始录制。低于20db超过4秒,停止录制

语音实时检测

<template><div class="auto-recorder"><canvas ref="canvas"></canvas><button @click="toggleRecording" :disabled="isRecording">{{ isRecording ? '录音中' : '未录音' }}</button><audio v-if="audioUrl" controls :src="audioUrl"></audio></div>
</template><script>export default {data() {return {isRecording: false, //显示当前录音状态mediaRecorder: null,audioChunks: [], //存储录音数据的数组audioContext: null, //用于处理音频analyser: null, //用于分析音频频谱bufferSize: 2048, //用于设置分析音频的缓冲区大小。threshold: 0, //用于设置录音阈值的属性 dbcanvasContext: null, //用于存储 Canvas 上下文的属性。canvasWidth: 400,canvasHeight: 200,silentTime: 0, //记录静音时间audioUrl: '',silenceInterval: null //计时器ID};},mounted() {this.setupAudio();this.setupCanvas();this.draw();},methods: {async setupAudio() { //异步方法,用于设置音频环境。//使用 navigator.mediaDevices.getUserMedia 方法请求用户媒体设备(麦克风)的权限//返回一个 MediaStream 对象。const stream = await navigator.mediaDevices.getUserMedia({audio: true});//创建一个新的 AudioContext 对象,用于处理音频。this.audioContext = new AudioContext();//创建 AnalyserNode 对象,用于分析音频频谱。this.analyser = this.audioContext.createAnalyser();//创建一个 MediaStreamAudioSourceNode 对象,表示从媒体流中读取数据。const microphone = this.audioContext.createMediaStreamSource(stream);//将媒体流连接到分析器节点。microphone.connect(this.analyser);//设置 FFT(Fast Fourier Transform)大小,用于控制音频数据的分析精度。this.analyser.fftSize = this.bufferSize;},//设置 Canvas 元素的方法setupCanvas() {// 获取组件中引用的 Canvas 元素。const canvas = this.$refs.canvas;// 获取 Canvas 2D 上下文,用于绘制图形。this.canvasContext = canvas.getContext('2d');// 设置 Canvas 宽度。canvas.width = this.canvasWidth;// 设置 Canvas 高度。canvas.height = this.canvasHeight;// 设置画布背景颜色为黑色。this.canvasContext.fillStyle = '#000';// 绘制一个填充矩形,覆盖整个画布,用黑色填充。this.canvasContext.fillRect(0, 0, this.canvasWidth, this.canvasHeight);},//这是一个负责绘制音频波形图的函数,并且会检查声音的分贝是否超过阈值。draw() {//请求下一帧动画requestAnimationFrame(this.draw);if (this.analyser != null) { //当前不在录音的情况// 获取音频频域数据的数组长度const bufferLength = this.analyser.frequencyBinCount;// 创建一个无符号 8 位整数数组,用于存储音频频域数据const dataArray = new Uint8Array(bufferLength);// 从 AnalyserNode 获取音频频域数据this.analyser.getByteTimeDomainData(dataArray);// 设置画布的填充颜色为黑色this.canvasContext.fillStyle = '#000';// 绘制一个黑色的矩形,覆盖整个画布,用于清空画布this.canvasContext.fillRect(0, 0, this.canvasWidth, this.canvasHeight);// 设置绘制线条的宽度为 2 像素this.canvasContext.lineWidth = 2;// 设置绘制线条的颜色为绿色this.canvasContext.strokeStyle = '#00ff00';// 开始绘制新路径this.canvasContext.beginPath();// 计算每个数据片段在画布上的宽度const sliceWidth = this.canvasWidth * 1.0 / bufferLength;// 初始化 x 坐标let x = 0;// 遍历音频频域数据数组,绘制波形图for (let i = 0; i < bufferLength; i++) {// 将音频数据归一化到 [-1, 1] 的范围const v = dataArray[i] / 128.0;// 根据归一化的数据计算波形图在画布上的 y 坐标const y = v * this.canvasHeight / 2;// 如果是第一个数据点,则将画笔移动到该点;否则,从上一个点绘制直线到当前点if (i === 0) {this.canvasContext.moveTo(x, y);} else {this.canvasContext.lineTo(x, y);}// 更新 x 坐标x += sliceWidth;}// 绘制直线到画布的右边中央this.canvasContext.lineTo(this.canvasWidth, this.canvasHeight / 2);// 绘制路径this.canvasContext.stroke();// 检测声音分贝是否超过阈值const average = this.calculateAverage(dataArray);// 如果平均值超过阈值if (average > this.threshold) {if (!this.isRecording) {this.startRecording(); //开始录音}this.silentTime = 0; //重置静音时间} else {if (this.isRecording) {// 开始计时,如果静音时间超过4秒,停止录音if (!this.silenceInterval) {this.silenceInterval = setInterval(() => {this.silentTime++;if (this.silentTime >= 4) {this.stopRecording();clearInterval(this.silenceInterval);this.silenceInterval = null;this.silentTime = 0; //重置静音时间}}, 1000);}}}}},// 定义了一个名为 calculateAverage 的方法,接受一个 dataArray 参数。calculateAverage(dataArray) {// 初始化一个 sum 变量用于存储音频数据的总和。let sum = 0;// 获取 dataArray 的长度并存储在 length 变量中。const length = dataArray.length;// 使用 for 循环遍历 dataArray。// 对于每个元素,减去128并加到 sum 上。// 这是因为音频数据范围在0到255之间,128表示音量的中值。for (let i = 0; i < length; i++) {sum += (dataArray[i] - 128);}// 返回音频数据的平均值。return sum / length;},// 控制音频录制的功能async toggleRecording() {if (!this.isRecording) {try {// 请求用户麦克风的音频输入const stream = await navigator.mediaDevices.getUserMedia({audio: true});// 创建 MediaRecorder 实例,用于录制音频this.mediaRecorder = new MediaRecorder(stream);// 添加事件监听器,当有音频数据可用时,将其存储到 audioChunks 中// 保存数据(数据有效)this.mediaRecorder.addEventListener('dataavailable', event => {if (event.data.size > 0) {this.audioChunks.push(event.data);}});// 开始录制媒体流this.mediaRecorder.start();this.isRecording = true;} catch (error) {console.error('录音失败...', error);}}},async stopRecording() {// 停止录制媒体流。this.mediaRecorder.stop();// 等待 MediaRecorder 停止录制完成await new Promise(resolve => {this.mediaRecorder.addEventListener('stop', resolve);});// 创建一个 Blob 对象,用于存储录制的音频数据const blob = new Blob(this.audioChunks, {type: 'audio/pcm'});console.log('Blob size:', blob.size);if (blob.size > 0) {console.log('Blob contains audio data');} else {console.log('Blob is empty');}this.audioUrl = URL.createObjectURL(blob);console.log("this.audioChunks", this.audioChunks);console.log("已发送给后端!");// 将录音数据发送给后端// 这里需要实现发送录音数据给后端的逻辑console.log('录音数据', blob);// 重置录音状态和录音数据数组this.isRecording = false;this.audioChunks = [];},async startRecording() {this.toggleRecording();console.log('录音中');}}};
</script><style>.auto-recorder {display: flex;flex-direction: column;align-items: center;}canvas {margin-top: 20px;}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/16312.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mysql中的约束(常见约束、外键约束)

约束的定义 约束就是对于数据库的表中字段&#xff0c;在某些性质上进行约束&#xff0c;以规范化字段或者实现一些功能。 常见的约束 首先我们先创建一个用于存储员工和所对应公司的数据库。 mysql> create database employee_company; Query OK, 1 row affected (0.01…

【开源可视化报表设计器】借力实现高效率流程化办公!

进行数字化转型、实现流程化办公&#xff0c;这些应该是目前很多企业都想要实现的目标吧。那么&#xff0c;利用什么样的软件平台可以实现&#xff1f;低代码技术平台拥有可视化界面、灵活操作、好维护等众多优势特点&#xff0c;可以借助低代码技术平台、开源可视化报表设计器…

游戏缺失steam_api64.dll的修复方法,快速解决游戏启动问题

在现代科技发展的时代&#xff0c;电脑已经成为我们生活中不可或缺的一部分。然而&#xff0c;在使用电脑的过程中&#xff0c;我们经常会遇到一些常见的问题&#xff0c;其中之一就是找不到某个特定的动态链接库文件&#xff0c;比如steamapi64.dll。这个问题可能会导致某些应…

深度学习中的优化算法二(Pytorch 19)

一 梯度下降 尽管梯度下降&#xff08;gradient descent&#xff09;很少直接用于深度学习&#xff0c;但了解它是理解下一节 随机梯度下降算法 的关键。例如&#xff0c;由于学习率过大&#xff0c;优化问题可能会发散&#xff0c;这种现象早已在梯度下降中出现。同样地&…

民国漫画杂志《时代漫画》第25期.PDF

时代漫画25.PDF: https://url03.ctfile.com/f/1779803-1248635084-fd4794?p9586 (访问密码: 9586) 《时代漫画》的杂志在1934年诞生了&#xff0c;截止1937年6月战争来临被迫停刊共发行了39期。 ps: 资源来源网络!

03:PostgreSQL逻辑结构(表空间、数据库、模式、表、索引)

环境规划&#xff1a; 操作系统&#xff1a;CentOS 7.9 64bitPostgreSQL 版本&#xff1a;16.x 或 15.x安装用户&#xff1a;postgres软件安装目标路径&#xff1a;/usr/pgsql-<version>数据库数据目录&#xff1a;/pgdata 目录 表空间Tablespace 默认表空间 手动创建…

RBAC 动态权限

文章目录 前言一、RBAC&#xff08;Role-Based Access Control&#xff0c;基于角色的访问控制&#xff09;二、Java实现RBAC 权限的大概思路1. 添加依赖2. 配置MyBatis-Plus和数据源1. 添加依赖2. 实体类与Mapper接口UserMapper.java 3. 配置MyBatis-Plus4. 自定义UserDetails…

民国漫画杂志《时代漫画》第15期.PDF

时代漫画15.PDF: https://url03.ctfile.com/f/1779803-1247458444-8befd8?p9586 (访问密码: 9586) 《时代漫画》的杂志在1934年诞生了&#xff0c;截止1937年6月战争来临被迫停刊共发行了39期。 ps:资源来源网络&#xff01;

【LeetCode:2769. 找出最大的可达成数字 + 模拟】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

Nodejs(文件操作,构建服务器,express,npm)

文章目录 文件操作1.读取文件1&#xff09;步骤2&#xff09;范例 2.写文件1&#xff09;步骤2&#xff09;范例 3.删除文件4.重命名文件夹5删除文件夹 Url1.url.parse()2.url.fomat() Query1.query.parse()2.query.stringfy()3.编码和解码 第三方模块1.nodemailer2.body-parse…

BUUCTF-Misc24

从娃娃抓起1 1.打开附件 是两个文本文件 2.电报码 电报码在线翻译网站&#xff1a;https://usetoolbar.com/convert/cccn.html 3.汉字五笔编码 汉字五笔编码在线网站查询&#xff1a;https://www.qqxiuzi.cn/bianma/wubi.php 4.转化为MD5值 将文字保存到文本文档 用winR输入…

部署ELK日志分析系统——超详细

ELK日志分析系统 文章目录 ELK日志分析系统资源列表基础环境一、环境准备二、部署Elasticsearch软件2.1、安装Elasticsearch软件2.2、加载系统服务2.3、更改Elasticsearch主配置文件2.4、创建数据存放路径并授权2.5、启动Elasticsearch2.6、查看节点信息 三、安装Elasticsearch…

PDF转word 免费软件推荐

超级PDF 免费的 PDF转Word|在线免费PDF转Word - 超级PDF 知乎推荐&#xff0c;还没试用&#xff1a; https://zhuanlan.zhihu.com/p/614750512?utm_id0

【WEEK13】 【DAY5】Shiro第五部分【中文版】

2024.5.24 Friday 接上文【WEEK13】 【DAY4】Shiro第四部分【中文版】 目录 15.7.Shiro请求授权的实现15.7.1.修改ShiroConfig.java15.7.1.1.添加一行验证授权的代码15.7.1.2.重启 15.7.2.修改MyController.java15.7.3.修改ShiroConfig.java15.7.4.重启15.7.5.修改UserRealm.ja…

Jenkins 动态salve简单配置连接 EKS

安装Jenkins helm repo add jenkins https://charts.jenkins.io helm repo update # 当前版本 jenkins-5.1.18.tgz瘦身后的 values.yaml # grep -Ev ^\s*#|^$ values.yaml nameOverride: fullnameOverride: namespaceOverride: clusterZone: "cluster.local" kubern…

C#对文件进行批量重命名或者对某个单独的文件进行改名

目录 一、FolderBrowserDialog 二、OpenFileDialog 三、Path 四、ui设计 五、代码部分 一、FolderBrowserDialog FolderBrowserDialog是一个用于选择文件夹的对话框控件&#xff0c;可以在windows Forms应用程序中使用。使用它可以让用户选择一个文件夹&#xff0c;并返…

Kubernetes 之硬盘持久化和 EmptyDir 与 HostPath 挂载类型

Kubernetes 之硬盘持久化和 EmptyDir 与 HostPath 挂载类型 持久化存储的意义 在 Kubernetes 中部署的应用都是以 Pod 的方式运行的。大部分情况这些 Pod 是无状态的。但是假如我们部署数据库或者需要一个存放关键文件的文件夹的时候&#xff0c;这时候我们就需要存储持久化以…

ShardingSphere如何完成与Spring家族无缝整合的?

系统集成即ShardingSphere 和 Spring 框架的集成。 ShardingSphere 实现两种系统集成机制&#xff1a; 命名空间&#xff08;namespace&#xff09;机制&#xff0c;通过扩展 Spring Schema 来实现与 Spring 框架的集成编写自定义 starter 组件完成与 Spring Boot 的集成 1 …

需求:实现一个可以统计代码的运行时间

需求&#xff1a;有一个做加法计算的函数&#xff0c;要统计执行这个加法函数代码运行了多久 import timedef add(a, b):time.sleep(1)return a bst time.time() add(100, 200) et time.time() print("该函数运行时间为&#xff1a;", et - st) 学了闭包&#x…

文件批量替换

替换yaml文件中的kube-system成ck find . -type f -name "*.yaml" -exec sed -i s/kube-system/ck/g {} \;