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,所以会导致程序崩溃

后续实现音频图

兄弟萌给个关注~

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

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

相关文章

建设Web3需要Web2的人才?探索传统技能在Web3时代的作用

摘要&#xff1a;Web3作为下一代互联网技术的前沿&#xff0c;许多人关注着它的发展和应用。然而&#xff0c;建设Web3是否需要Web2的人才仍然是一个有争议的问题。 Web3作为下一代互联网技术&#xff0c;以去中心化、智能合约和用户自治等特点引起了广泛的关注。与此同时&…

JAVA1

文章目录 计算机的硬件与软件DOS命令 计算机的硬件与软件 DOS命令

大数据开发基础-环境配置篇-Hadoop集群安装

鼠鼠接下来将更新一系列自己在学习大数据开发过程中收集的资源、和自己的总结、以及面经答案、LeetCode刷题分析题解。 首先是大数据开发基础篇 环境搭建、组件面试题等 其次是更新大数据开发面经的java面试基础 最后更新一个大数据开发离线数仓的实战项目&#xff0c;自己写入…

Redis的数据类型及对应的数据结构(二)

接上篇&#xff1a;Redis的数据类型及对应的数据结构&#xff08;一&#xff09;_鱼跃鹰飞的博客-CSDN博客 本篇主要讨论剩下的几种数据结构的应用场景 应用场景 集合的主要几个特性&#xff0c;无序、不可重复、支持并交差等操作。 因此 Set 类型比较适合用来数据去重和保…

kafka生产者api和数据操作

Kafka 生产者 发送流程 消息发送过程中涉及到两个线程——main线程和Sender线程 main线程 使用serializer&#xff08;并非java默认&#xff09;序列化数据&#xff0c;使用partitioner确认发送分区 在main线程中创建了一个双端队列RecordAccumulator&#xff0c;main线程将…

【半监督医学图像分割 2023 CVPR】BCP

【半监督医学图像分割 2023 CVPR】BCP 论文题目&#xff1a;Bidirectional Copy-Paste for Semi-Supervised Medical Image Segmentation 中文题目&#xff1a;双向复制粘贴半监督医学图像分割 论文链接&#xff1a;https://arxiv.org/abs/2305.00673 论文代码&#xff1a;http…

新星计划2023【Java基础及数据库Mysql】学习方向报名入口!

新星计划2023【Java基础及数据库Mysql】学习方向报名入口&#xff01; 一、关于本学习方向导师二、关于本学习方向官方微信群三、关于活动时间&奖品&要求四、学习计划五、TOP5评选规则六、活动要求七、注意事项 本赛道是针对那些希望从事Java开发并且想要学习如何与数据…

【TCP/IP】多进程服务器的实现(进阶) - 多进程服务器模型及代码实现

经过前面的铺垫&#xff0c;我们已经具备实现并发服务器的基础了&#xff0c;接下来让我们尝试将之前的单任务回声服务器改装成多任务并发模式吧&#xff01; 多任务回声服务器模型 在编写代码前&#xff0c;先让我们大致将多任务&#xff08;回声&#xff09;服务器的模型抽象…

Jenkins自动化构建

自动化构建 Jenkins 是一款开源 CI&CD 软件&#xff0c;用于自动化各种任务&#xff0c;包括构建、测试和部署软件 Jenkins 支持各种运行方式&#xff0c;可通过系统包、Docker 或者通过一个独立的 Java 程序 安装依赖 安装参考&#xff1a;Windows环境下安装Jenkins **…

MySQL数据库——高级查询语句

MySQL数据库——高级查询语句 一、数据库查询二、高效查询方式1.指定指字段进行查询——SELECT2.对字段进行去重查询——DISTINCT3.条件查询——where3.逻辑关系的增加查询——and 和 or4.已知值的数据记录查询——IN5.范围内数据记录查询——BETWEEN6.通配符查询7.关键字排序查…

ng-zorro select Multiple selection 一行展示

问题&#xff1a; ng-zorro 的多项选择组件&#xff0c;选完选项之后不要换行展示&#xff0c;不换行&#xff0c;超出隐藏或者可滚动。这个问题的关键点在于&#xff1a; 各个选项数据字符串长度不确定&#xff0c;不能够准确知道当前容器最大能够渲染多少个选项&#xff0c;…

微服务如何治理

微服务远程调用可能有如下问题&#xff1a; 注册中心宕机&#xff1b; 服务提供者B有节点宕机&#xff1b; 服务消费者A和注册中心之间的网络不通&#xff1b; 服务提供者B和注册中心之间的网络不通&#xff1b; 服务消费者A和服务提供者B之间的网络不通&#xff1b; 服务提供者…