Audio API 实现音频播放器

news2024/11/24 9:37:23

市面上实现音频播放器的库有很多,比如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,所以会导致程序崩溃

后续实现音频图

兄弟萌给个关注~

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

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

相关文章

软件工程是否迎来iPhone时刻?

“软件工程是否迎来iPhone时刻&#xff1f;” 是2023K全球软件研发行业创新峰会上海站主会场的Panel discussion的主题&#xff0c;出场的几位嘉宾给出了不同的答案&#xff0c;其中有两位嘉宾给出了“No”&#xff0c;一位给出了“塞班时刻”&#xff08;后来给我朋友圈投票是…

设计模式—“状态变化”

在组件构建过程中,某些对象的状态经常面临变化,如何对这些变化进行有效的管理?同时又维持高层模块的稳定?"状态变化"模式为这一问题提供了解决方案。 典型模式有:Memento、State 一、State 动机 在软件构建过程中,某些对象的状态如果改变,其行为也会随之而…

18-BOM对象

一、是什么 &#x1f355;&#x1f355;&#x1f355;BOM (Browser Object Model)&#xff0c;浏览器对象模型&#xff0c;提供了独立于内容与浏览器窗口进行交互的对象 其作用就是跟浏览器做一些交互效果,比如如何进行页面的后退&#xff0c;前进&#xff0c;刷新&#xff0…

SSM幼儿园管理系统的设计与实现-计算机毕设 附源码86673

SSM幼儿园管理系统的设计与实现 摘 要 21世纪时信息化的时代&#xff0c;几乎任何一个行业都离不开计算机&#xff0c;将计算机运用于幼儿管理系统也是十分常见的。过去使用手工的管理方式对幼儿园进行管理&#xff0c;造成了管理繁琐、难以维护等问题&#xff0c;如今使用计算…

搜索表单的触发方式

1、按键盘触发 配套监听 _keydownHandler: function (event) { // 获取表单数据 let formValue this.$[frm-mach-break].serializeMyForm(); let params { machineName: formValue.mach_id }; this.requestAjax(ajx-view-mach-break, params); }, onKeypress: function ({ ke…

抖音seo账号矩阵系统源码sign解密.技术

抖音SEO矩阵系统源码是一种用于优化抖音视频内容的工具&#xff0c;可以帮助用户提高抖音视频的搜索排名和流量&#xff0c;从而增加视频曝光和转化率。该系统包括两部分&#xff0c;即数据收集和分析模块以及SEO策略和实施模块。 返回示例 错误&#xff1a; { "ec…

搜索算法(五) DFS BFS 练习题

练习题 1.力扣https://leetcode.cn/problems/surrounded-regions/这题和417类似&#xff0c;都是从边界朝内部搜索&#xff0c;417用的是DFS&#xff0c;这里为了练习&#xff0c;就用BFS。 首先从四条边界得到‘O’的坐标&#xff0c;加入队列。接着一层一层搜索&#xff0c…

11个AI绘画软件大全,赶紧收藏

随着人工智能技术的不断发展&#xff0c;越来越多的AI绘画软件应运而生。AI绘画软件利用人工智能技术&#xff0c;通过计算机自动生成或辅助生成艺术作品。 AI绘画软件通常集深度学习、计算机视觉、自然语言处理等技术于一体&#xff0c;可以模拟人类的创作过程&#xff0c;生…

带你详细了解Redis事务锁机制-加实列演示-加连接池-包括解决遗留问题-下

Redis_事务_锁机制_秒杀 连接池技术 连接池介绍 1、节省每次连接redis 服务带来的消耗&#xff0c;把连接好的实例反复利用。 2、链接池参数 MaxTotal&#xff1a;控制一个pool 可分配多少个jedis 实例&#xff0c;通过pool.getResource()来获取&#xff1b;如果赋值为-1&…

.NetCore gRpc 客户端与服务端的单工通信Demo

文章目录 .NetCore gRpc 客户端与服务端的单工通信Demo服务端方式一方式二 客户端proto协议文件syntax "proto3";import "google/protobuf/empty.proto";serviceproto3与.netCore 的类型对应日期和时间可为 null 的类型字节小数为 Protobuf 创建自定义 de…

<Linux开发>驱动开发 -Linux MISC 驱动

&#xff1c;Linux开发&#xff1e;驱动开发 -Linux MISC 驱动 交叉编译环境搭建&#xff1a; &#xff1c;Linux开发&#xff1e; linux开发工具-之-交叉编译环境搭建 uboot移植可参考以下&#xff1a; &#xff1c;Linux开发&#xff1e; -之-系统移植 uboot移植过程详细记…

软考高级系统架构设计师(二) 基础知识之计算机组成与系统结构2

目录 总线 ​CISC与RISC 流水线技术 总线 练习题&#xff1a; CISC与RISC RISC(精简指令集计算机)和CISC(复杂指令集计算机)是当前CPU的两种架构. RISC与CICS的比较 1.RISC比CICS更能提高计算机运算速度&#xff1b;RISC寄存器多&#xff0c;就可以减少访存次数&#xff0c;…

生产环境Java应用服务内存泄漏分析与解决

有个生产环境CRM业务应用服务&#xff0c;情况有些奇怪&#xff0c;监控数据显示内存异常。内存使用率99.%多。通过生产监控看板发现&#xff0c;CRM内存超配或内存泄漏的现象&#xff0c;下面分析一下这个问题过程记录。 1、服务器硬件配置部署情况 生产服务器采用阿里云ECS…

【命令参数】MSBuild - 环境配置及常用命令参数

目录 环境配置 基本语法 参数指令 对各类程序的命令参数的掌握是软件工程师必修课之一&#xff0c;它是通往自动化、高效化开发测试的必经之路。对于MSBuild&#xff0c;我们可以借助它以一种轻量级的形式去完成对于项目又或解决方案的生成&#xff0c;而避开使用繁大的IDE进…

Linux系统之部署Etherpad文档编辑器

Linux系统之部署Etherpad文档编辑器 一、Etherpad介绍1.Etherpad简介2.Etherpad特点 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、检查本地环境3.1 检查本地操作系统版本3.2 检查系统内核版本3.3 检查系统是否安装Node.js 四、部署Node.js 环境4.1 下载Node.js安装包…

【吴恩达老师《机器学习》】课后习题3之【逻辑回归解决多分类】与【神经网络】笔记(代码注释详细)

本次习题所用到的数据&#xff0c;#数据集&#xff1a;ex3data1.mat&#xff0c;参数&#xff1a;ex3weights.mat。在文章开头&#xff0c;下载即可&#xff01; 逻辑回归解决多分类问题 二分类VS多分类 在机器学习中&#xff0c;分类是一种监督学习任务&#xff0c;其中我们…

从机缘到成就

机缘 在这1825天的创作之旅中&#xff0c;我收获了许多宝贵的机遇和经验。起初&#xff0c;我只是一个对技术有着浓厚兴趣的普通人&#xff0c;遇到了一个在eclipse导入工程后出现中文乱码的问题。而我决定将这个问题记录下来&#xff0c;并分享给其他可能遇到相同困扰的人们。…

数据库系统概述——第三章 关系数据库标准语言SQL(知识点复习+练习题)

&#x1f31f;博主&#xff1a;命运之光 &#x1f984;专栏&#xff1a;离散数学考前复习&#xff08;知识点题&#xff09; &#x1f353;专栏&#xff1a;概率论期末速成&#xff08;一套卷&#xff09; &#x1f433;专栏&#xff1a;数字电路考前复习 &#x1f99a;专栏&am…

Linux基础内容(23)—— 信号补充与多线程交接知识

Linux基础内容&#xff08;22&#xff09;—— 信号_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/130835485 目录 1.可重入函数 1.情况假设 2.volatile 3.SIGCHLD信号 1.SIGCHLD介绍 2.信号的确认 3.wait的处理 1.可重入函数 1.情况假设…

插件 - 通过SPI方式实现插件管理

文章目录 SPI概念基本原理使用步骤优点缺点Code真实使用场景案例JDBC(Java Database Connectivity)Servlet API日志框架SPI概念 SPI(Service Provider Interface)是Java提供的一种服务扩展机制,它允许应用程序在运行时动态加载和发现提供者(Providers),并与它们进行交…