Audio API 实现音频播放器

市面上实现音频播放器的库有很多,比如wavesurfer.js、howler.js等等,但是都不支持大音频文件处理,100多M的文件就有可能导致程序崩溃。总之和我目前的需求不太符合,所以打算自己实现一个音频播放器,这样不管什么需求 在技术上都可控。下面我们简单介绍下wavesurferJs、和howlerJs的实现,然后再讲解如何利用audio API实现自定义语音播放器。

具体资源github下载

wavesurferJs

一开始选择wavesurferJs 主要是因为它的音频图功能。
效果如下:
在这里插入图片描述
是不是很漂亮 hh
下面是实现步骤:

  1. 初始化
this.playWavesurfer = WaveSurfer.create({
	container: '#waveform2',
	mediaType: 'audio',
	height: 43,
	scrollParent: false,
	hideScrollbar: true,
	waveColor: '#ed6c00',
	interact: true,
	progressColor: '#dd5e98',
	cursorColor: '#ddd5e9',
	interact: true,
	cursorWidth: 1,
	barHeight: 1,
	barWidth: 1,
	plugins: [
		WaveSurfer.microphone.create()
	]
});
  1. 动态加载音频地址
this.playWavesurfer.load(this.audioUrl);
  1. 设置加载loading和完毕后计算音频总时长
this.playWavesurfer.on('loading', (percent, xhr) => {
		this.audioLoadPercent = percent - 1;
	})
	this.playWavesurfer.on('ready', () => {
		this.audioLoading = false;
		const duration = this.playWavesurfer.getDuration();
		this.duration = this.formatTime(duration);
		this.currentTime = this.formatTime(0);
	})
  1. 播放中计算时长
this.playWavesurfer.on('audioprocess', function () {
	const duration = that.playWavesurfer.getDuration();
	const currentTime = that.playWavesurfer.getCurrentTime();
	that.currentTime = that.formatTime(currentTime);
	that.duration = that.formatTime(duration);
	if (that.currentTime === that.duration) {
		that.audioPlayingFlag = false;
	}
});
  1. 播放、暂停
this.playWavesurfer.playPause.bind(this.playWavesurfer)();
  1. 快进、快退
this.playWavesurfer.skip(15);
//this.playWavesurfer.skip(-15);
  1. 倍数播放
this.playWavesurfer.setPlaybackRate(value, true);

这样基本功能大概实现。

利用howlerJs实现

  1. 初始化、动态加载音频路径
this.howler = new Howl({
	src: [this.audioUrl]
});
  1. 加载完毕计算音频总时长
this.howler.on('load', () => {
	this.audioLoading = false;
	const duration = this.howler.duration();
	this.duration = this.formatTime(duration);
	this.currentTime = this.formatTime(0);
});

  1. 播放中获取当前时间
this.currentTime = this.formatTime(this.howler.seek());
  1. 播放完毕
this.howler.on('end', () => {
	this.audioPlayingFlag = false;
	this.siriWave2.stop();
	this.currentTime = "00:00:00";
	this.progressPercent = 0;
	cancelAnimationFrame(this.playTimer);
})
  1. 快进、快退
this.howler.seek(this.howler.seek() + 15);
//this.howler.seek(this.howler.seek() - 15);
  1. 设置倍数播放
this.howler.rate(value);
  1. 播放、暂停
this.howler.play();
// this.howler.pause();
  1. 手动定位播放时长
<div id="waveform2" ref="waveform2" @click="changProgress">
	<div class="bar" v-if="!audioLoading&&!audioPlayingFlag"></div>
	<div class="progress" :style="{width: `${progressPercent}`}"></div>
</div>
changProgress(e) {
	if (this.howler.playing()) {
		this.howler.seek((e.offsetX / this.$refs['waveform2'].offsetWidth)*this.howler.duration());
	}
},

这样基本功能大概实现。

利用audio API实现播放器

效果图:
在这里插入图片描述
动画库 暂时用的 siriwave.js

先定义audio标签隐藏,可以js里面动态生成

<audio :src="audioUrl" style="display: none;" controls ref="audio"></audio>
this.audio = this.$refs['audio'];
  1. 获取音频url后 动态加载 需要load一下
this.audio.load();
  1. 音频加载完毕
this.audio.addEventListener("canplaythrough", () => {
	this.audioLoading = false;
	console.log('music ready');
}, false);
  1. 监听可以播放后 计算音频时长
this.audio.addEventListener("canplay", this.showTime, false);
showTime() {
	if (!isNaN(this.audio.duration)) {
		this.duration = this.formatTime(this.audio.duration);
		this.currentTime = this.formatTime(this.audio.currentTime);
	}
},
  1. 播放中 时间改变计算当前 时间
this.audio.addEventListener("timeupdate", this.showTime, true);
  1. 监听播放事件
this.audio.addEventListener('play', () => {
	this.audioPlaying();
}, false);
  1. 播放完毕
this.audio.addEventListener('ended', () => {
	this.audioPlayingFlag = false;
	this.siriWave2.stop();
	this.currentTime = "00:00:00";
	this.progressPercent = 0;
	cancelAnimationFrame(this.playTimer);
}, false)
  1. 前进、后退
this.audio.currentTime += 15;
// this.audio.currentTime -= 15;
  1. 设置播放倍数
this.audio.playbackRate = value;
  1. 播放、暂停
this.audio.play();
// this.audio.pause();
  1. 音频定位
<div id="waveform2" ref="waveform2" @click="changProgress">
	<div class="bar" v-if="!audioLoading&&!audioPlayingFlag"></div>
	<div class="progress" :style="{width: `${progressPercent}`}"></div>
</div>

计算 定位时长

changProgress(e) {
	// if (this.audioPlayingFlag) {
		this.audio.currentTime = (e.offsetX / this.$refs['waveform2'].offsetWidth)*this.audio.duration;
		this.progressPercent = ((this.audio.currentTime/this.audio.duration) * 100) + '%';
	// }
},
  1. siri动画实现
this.siriWave = new SiriWave({
	container: that.$refs['waveform'],
		height: 43,
		cover: true,
		color: '#ed6c00',
		speed: 0.03,
		amplitude: 1,
		frequency: 6
	});

开启动画、停止动画

this.siriWave.start();
// this.siriWave.stop();

这样基本功能大概实现。 即使加载再大音频文件也不会卡。

踩坑

在这里插入图片描述
这里遇到一个大坑就是 audio自带,音频播放定位功能,在外面浏览器和vscode主体代码里面都可以定位,偏偏我在vscode插件里面不可以定位,会自动归0。翻遍了文档在MDN上找到这样一段描述:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Configuring_servers_for_Ogg_media#Handle_HTTP_1.1_byte_range_requests_correctly

Handle HTTP 1.1 byte range requests correctly
In order to support seeking and playing back regions of the media that aren’t yet downloaded, Gecko uses HTTP 1.1 byte-range requests to retrieve the media from the seek target position. In addition, Gecko uses byte-range requests to seek to the end of the media (assuming you serve the Content-Length header) in order to determine the duration of the media.
Your server should accept the Accept-Ranges: bytes HTTP header if it can accept byte-range requests. It must return 206: Partial content to all byte range requests; otherwise, browsers can’t be sure you actually support byte range requests.
Your server must also return 206: Partial Content for the request Range: bytes=0- as well.

经验证是和response header有关的。我通过对MP3资源set不同的response header来验证,结果如下(貌似segmentfault不支持markdown的表格,所以下面排版有点乱。):
ie
Content-Type 必须,当我设为audio/mpeg时才能播放,设为application/octet-stream不能。
Content-Length必须。和Accept-Ranges无关。

chrome
Content-Type 无关,设为application/octet-stream可以播放。
Content-LengthAccept-Ranges必须都有才可更改 currentTime。

也就是说ie需要response header 有正确的Content-TypeContent-Length
chrome需要头部有Content-LengthAccept-Ranges

然后我想到 vscode插件系统使用了 Service Worker

const headers = {
		'Content-Type': entry.mime,
		'Content-Length': entry.data.byteLength.toString(),
		'Access-Control-Allow-Origin': '*',
	};

果然 没有添加 Accept-Ranges字段

const headers = {
	'Content-Type': entry.mime,
	'Content-Length': entry.data.byteLength.toString(),
	'Access-Control-Allow-Origin': '*',
};

/**
 * @author lichangwei
 * @description 音频额外处理 否则无法调节进度
 * https://developer.mozilla.org/en-US/docs/Web/HTTP/Configuring_servers_for_Ogg_media#Handle_HTTP_1.1_byte_range_requests_correctly
 */

if (entry.mime === 'audio/mpeg') {
	headers['Accept-Ranges'] = 'bytes';
}

添加后就可以使用了。

后续

看了一下印象笔记的语音笔记实现。
在这里插入图片描述

在这里插入图片描述
印象笔记是如何避免大文件处理的呢

  • 首先录音过程中绘制的是真的音频线
  • 录音结束后是用假的音频线替代的

其实录制过程中实时处理音频数据还好,不至于导致浏览器崩溃,录制后生成的音频文件处理数据量太大了,内存直接飙升2-3G,所以会导致程序崩溃

后续实现音频图

兄弟萌给个关注~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

NLP入门:word2vec self-attention transformer diffusion的技术演变

这一段时间大模型的相关进展如火如荼&#xff0c;吸引了很多人的目光&#xff1b;本文从nlp领域入门的角度来总结相关的技术路线演变路线。 1、introduction 自然语言处理&#xff08;Natural Language Processing&#xff09;&#xff0c;简称NLP&#xff0c;是通过统计学、…

使用Docker Swarm部署PXC+HAProxy高可用集群(三节点)

使用Docker Swarm部署PXCHAProxy高可用集群&#xff08;三节点&#xff09; 1. 部署规划 当前规划中&#xff0c;只启动一个HAProxy服务&#xff0c;主要用来做MySQL节点的负载均衡和代理&#xff0c;但是HAProxy可能会出现单点故障&#xff0c;后续需要启动多个HAProxy节点&…

【网络知识面试】初识协议栈和套接字及连接阶段的三次握手

接上一篇&#xff1a;【网络面试必问】浏览器如何委托协议栈完成消息的收发 1. 协议栈 一直对操作系统系统的内核协议栈理解的模模糊糊&#xff0c;借着这一篇博客做一下简单梳理。 我觉得最直白的理解&#xff0c;内核协议栈就是操作系统中的一个网络控制软件&#xff0c;就是…

Web测试的主要内容和测试方法有哪些?

Web测试的主要内容&#xff1a; 一、输入框 二、搜索功能 三、增加、修改功能 四、删除功能 五、注册、登录模块 六、上传图片测试 七、查询结果列表 八、返回键检查 九、回车键检查 十、刷新键检查 Web测试的测试方法&#xff1a; 1.在测试时&#xff0c;与网络有关的步骤或者…

mybatis-plus在实际开发中的应用

文章目录 前言一、实体类的注解二、Req查询条件三、Controller接口四、Service接口五、Service接口实现类六、Mapper接口七、枚举的使用总结 前言 最近的项目是使用mybatis-plus作为持久层框架&#xff0c;前面也记录过mybatis-plus的基本使用&#xff0c;此次记录一下本次项目…

蓝桥杯专题-试题版-【01字符串】【2n皇后问题】【A+B问题】【Fibonacci数列】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

Python:pyecharts可视化

文章目录 简介Geo地理图绘制地图下载 折线图区域突出显示横坐标带选择展示 add地图Mapformatter控制value显示在图中显示value值目标html的解析自定义地图js资源原生地图js的解析解决省份上文字不居中的问题 桑基图设置桑基柱的颜色 参考文献 简介 &#xff08;这是20年的笔记…

【MySQL】库的操作

目录 一、创建数据库 二、字符集和校验规则 1、查看系统默认字符集以及校验规则 2、创建数据库案例 3、校验规则对数据库的影响 3.1、不区分大小写 3.2、区分大小写 3.3、进行查询 3.3.1、不区分大小写的查询以及结果 3.3.2、区分大小写的查询以及结果 3.4、结果排序…

flutter - 编写 阿里云-金融级实名认证插件

项目中有实名认证的需求&#xff0c;用户上传身份证反正面&#xff0c;进行人脸核验&#xff0c;后台集成的是阿里云的金融级实名认证SDK&#xff0c;巧合的是阿里云没有packages 需要自己造轮子。 废话不多少&#xff0c;直接上代码&#xff1a; 新建项目 ProjectType Plugin…

wsl下面的子系统启用systemctl

下载地址 https://github.com/gdraheim/docker-systemctl-replacement 操作 mv /usr/bin/systemctl /usr/bin/systemctl.old #对原文件进行备份sudo scp /mnt/c/Users/Administrator/Desktop/systemctl.py /usr/bin/systemctl #把项目中的systemctl.py文件拷贝到/use/bin/ 目…

Diffusion扩散模型学习2——Stable Diffusion结构解析-以文本生成图像为例

Diffusion扩散模型学习2——Stable Diffusion结构解析 学习前言源码下载地址网络构建一、什么是Stable Diffusion&#xff08;SD&#xff09;二、Stable Diffusion的组成三、生成流程1、文本编码2、采样流程a、生成初始噪声b、对噪声进行N次采样c、单次采样解析I、预测噪声II、…

安装mmdetection2.22(windows下)

安装mmdetection2.22 确定版本安装mmcv1.4安装mmdetection测试方案1方案2 确定版本 安装mmcv1.4 首先.cuda,pytorch得安装好&#xff0c;这里我拷贝pt1.8虚拟环境 安装mmcv1.4 安装mmdetection 参考文章 下载 cd E:\Code\mmdetection\mmdetection-2.22.0 pip install -r…

MATLAB 之 低层绘图操作和光照及材质处理

这里写目录标题 一、低层绘图操作1. 曲线对象2. 曲面对象3. 文本对象4. 其他核心对象4.1 区域块对象4.2 方框对象 二、光照和材质处理1. 光照处理2. 材质处理2.1 图形对象的反射特性2.2 material 函数 一、低层绘图操作 MATLAB 将曲线、曲面、文本等图形均视为对象&#xff0c…

【MySQL数据库 | 第十九篇】SQL性能分析工具

目录 前言&#xff1a; SQL执行频率&#xff1a; 慢查询日志&#xff1a; profile&#xff1a; profile各个指令&#xff1a; 总结&#xff1a; 前言&#xff1a; 本篇我们将为大家讲解SQL性能的分析工具&#xff0c;而只有熟练的掌握了性能分析的工具&#xff0c;才可以更…

【Spring Cloud Sleuth 分布式链路跟踪】 —— 每天一点小知识

&#x1f4a7; S p r i n g C l o u d S l e u t h 分布式链路跟踪 \color{#FF1493}{Spring Cloud Sleuth 分布式链路跟踪} SpringCloudSleuth分布式链路跟踪&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云…

力扣 257. 二叉树的所有路径

题目来源&#xff1a;https://leetcode.cn/problems/binary-tree-paths/description/ C题解1&#xff1a;使用递归&#xff0c;声明了全局变量result&#xff0c;遇到叶子节点就将字符串添加到result中。 递归三步法&#xff1a; 1. 确认传入参数&#xff1a;当前节点已有路径…

如何保证API接口的安全性

API接口的安全性是非常重要的&#xff0c;以下是一些保证API接口安全性的措施&#xff1a; 用户认证、授权&#xff1a;接口的调用者必须提供有效的身份认证信息&#xff0c;包括用户名、密码、密钥等&#xff0c;以保证接口的调用者的身份有效性。同时&#xff0c;需要在接口的…

echarts的基础知识和配置项

异步数据加载和更新 ECharts 中在异步更新数据的时候需要通过series的name属性对应到相应的系列&#xff0c;如果没有name&#xff0c;series就会根据数组的顺序索引&#xff0c;把数据跟前面的配置对应上 loading动画 如果数据加载时间较长&#xff0c;一个空的坐标轴放在画…

基于深度学习的高精度人脸口罩检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度人脸口罩检测识别系统可用于日常生活中或野外来检测与定位人脸口罩目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的人脸口罩目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5…

《面试1v1》Redis持久化

&#x1f345; 作者简介&#xff1a;王哥&#xff0c;CSDN2022博客总榜Top100&#x1f3c6;、博客专家&#x1f4aa; &#x1f345; 技术交流&#xff1a;定期更新Java硬核干货&#xff0c;不定期送书活动 &#x1f345; 王哥多年工作总结&#xff1a;Java学习路线总结&#xf…