AudioContext技术和音乐可视化(1)
写在最前,测试博客在这里,直接欣赏完成可视化效果。代码不日在github公开,性能目前巨烂,RadialGradient损耗巨大,优化正在提上日程。
转载注明来源。
扒掉网页上js的烦请留下js里的顶端注释谢谢。。虽然我代码是写的挺烂的。如果转发到别的地方了能注明一下作者和来源的话我会很开心的。
https://th-zxj.club 这是你从未体验过的船新版本
Intro
因为自己搭了个博客,一时兴起,就想写个动态的博客背景。毕竟用django后端渲染,前端只有jquery和bootstrap已经够low了,虽说极简风格也很棒,但是多少有点亮眼的东西才好和别人吹牛不是吗。
为了方便讲解,整个思路分为两个部分:音乐播放和背景绘制。
一、音乐播放
1.1 AudioContext
概述部分懒得自己写,参考MDN的描述。
AudioContext接口表示由音频模块连接而成的音频处理图,每个模块对应一个
AudioNode
。AudioContext可以控制它所包含的节点的创建,以及音频处理、解码操作的执行。做任何事情之前都要先创建AudioContext对象,因为一切都发生在这个环境之中。
1.2 浏览器支持状况
AudioContext标准
目前还是草案,不过新chrome已经实现了。我使用的chrome版本如下。
版本 70.0.3538.77(正式版本) (64 位)
如果发现console报错或者其他问题请检查浏览器版本,所有支持的浏览器可以在这个链接查看。
1.3 AudioContext和音频处理图
关于AudioContext
我的了解不是很深入,所以只在需要用到的部分进行概述。
首先,关于音频处理图的概念。
这个名词不甚直观,我用过虚幻,所以用虚幻的Blueprint
来类比理解。音频处理图,其实是一系列音频处理的模块,连接构成一张数据结构中的“图”,从一般使用的角度来讲,一个播放音频的图,就是AudioSource -> AudioContext.destination
,两个节点构成的图。其中有很多特殊的节点可以对音频进行处理,比如音频增益节点GainNode
。
对于音频处理的部分介绍就到这里为止,毕竟真的了解不多,不过从MDN的文档看,可用的处理节点还是非常多的,就等标准制订完成了。
1.4 加载音频文件并播放
音频文件加载使用典型的JavaScript
接口FileReader
实现。
一个非常简单的实例是这样
首先是html里写上input
<html> <body> <input type="file" accept="audio/*" onchange="onInputChange"> </body> </html>
然后在javascript里读文件内容。
function onInputChange(files){ const reader = new FileReader(); reader.onload = (event) => { // event.target.result 就是我们的文件内容了 } reader.readAsArrayBuffer(files[0]) }
文件读取就是这么简单,所以回到那个问题:说了那么多,音乐到底怎么放?
答案是用AudioContext
的decodeAudioData
方法。
所以从上面的js里做少许修改——
// 创建一个新的 AudioContext const ctx = new AudioContext(); function onInputChange(files){ const reader = new FileReader(); reader.onload = (event) => { // event.target.result 就是我们的文件内容了 // 解码它 ctx.decodeAudioData(event.target.result).then(decoded => { // 解码后的音频数据作为音频源 const audioBufferSourceNode = ctx.createBufferSource(); audioBufferSourceNode.buffer = decoded; // 把音源 node 和输出 node 连接,boom—— audioBufferSourceNode.connect(ctx.destination); audioBufferSourceNode.start(0); // 收工。 }); } reader.readAsArrayBuffer(files[0]) }
1.5 分析频谱
频谱的概念我建议搜一下傅里叶变换,关于时域和频域转换的计算过程和数学原理直接略(因为不懂),至今我还只理解到时域和频域的概念以及傅里叶变换的实现接受采样返回采样数一半长的频域数据......
不班门弄斧了。
以前写python
的时候用的numpy
来进行傅里叶变换取得频域数据,现在在浏览器上用js着实有些难受。不过幸好,AudioContext
直接支持了一个音频分析的node,叫做AudioAnalyserNode
。
这个Node处于音源Node和播放输出Node之间,想象一道数据流,音源Node把离散的采样数据交给Analyser,Analyser再交给输出Node。
直接看代码实例。
// 创建一个新的 AudioContext const ctx = new AudioContext(); // 解码后的音频数据作为音频源 // 为了方便管理,将这些Node都放置在回调函数外部 const audioBufferSourceNode = ctx.createBufferSource(); // 创建音频分析Node! const audioAnalyser = ctx.createAnalyser(); // 注意注意!这里配置傅里叶变换使用的采样窗口大小!比如说,我们要256个频域数据,那么采样就应该是512。 // 具体对应频率请自行搜傅里叶变换相关博文。 audioAnalyser.fftSize = 512; function onInputChange(files){ const reader = new FileReader(); reader.onload = (event) => { // event.target.result 就是我们的文件内容了 // 解码它 ctx.decodeAudioData(event.target.result).then(decoded => { // 停止原先的音频源 audioBufferSourceNode.stop(); // 先把音频源Node和Analyser连接。 audioBufferSourceNode.connect(audioAnalyser); // 然后把Analyser和destination连接。 audioAnalyser.connect(ctx.destination); // 修改音频源数据 audioBufferSourceNode.buffer = decoded; audioBufferSourceNode.start(0); // 收工。 }); } reader.readAsArrayBuffer(files[0]) } window.requestAnimationFrame(function() { // 读取频域数据 const freqData = new Uint8Array(audioAnalyser.frequencyBinCount); console.log(freqData); })
频域数据是二维的,频率(数组下标)和能量(下标对应值)。悄悄补一句,数学上应该说是该频率函数图像的振幅?
其实获得了这个频域数据,继续画出我们常见的条状频域图就很容易了。参考我一朋友的博客。misuzu.moe,可以看看效果。
关于AudioContext
的介绍先到此为止,等我找时间继续写。
PS:代码不保证复制粘贴就能运行,领会精神,遇到问题查查文档。MDN比我这博客详细多了。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
ngrok内网穿透服务部署记录
ngrok,一个用于实现内网穿透服务,golang写的,已经很久远的一个东西了,可自己部署的版本最后一个版本是1.7.1,很久也没更新了,但他还是比较稳妥的,基于自己生成openssl证书来编译生成服务器端和客户端,还是具有一定程度的安全性。 一年前,其实已经部署起来了,还专门在码云上fork github的源码,将部署生成的脚本做了一个分支:https://gitee.com/janpoem/ngrok-fork。最近因为要大量迁移主机,所以需要重新配置一次,因此这里就作为一个记录吧。 特别说明,基于他已经不会再更新了,所以还是不要用于正式发布的环境,开发环境使用,完全足够(一年下来,非常稳定,连重启都没试过)。 安装go1.7.6 首先,最后这个版本的ngrok,必须使用go1.7.6(1.7的last版本),下载地址:https://dl.google.com/go/go1.7.6.linux-amd64.tar.gz。然后在你的linux上部署go,嗯,部署go不用我再写说明了吧,go可能是这世上最容易部署的了。 好吧,还是写写,免得自己下次配置的时候忘记了: tar zvxf...
- 下一篇
如何给localStorage设置一个有效期?
引言 这个话题其实在上次分享<小程序填坑记里讲过了>已经讲过(大佬可绕过哦~),但后来QQ里/评论都有些同学,提到了一些疑问,问能否单独整理一篇更为详细的分享,讲解一下细节和完善提到的不足,如是有了下文。 这里是@IT·平头哥联盟,我是首席填坑官—苏南,用心分享 做有温度的攻城狮。 G众H:honeyBadger8,交-流:912594095 思考点 从我们接触前端起,第一个熟悉的存储相关的Cookie或者来分析我们生活中密切相关的淘宝、物流、闹钟等事物来说起吧, Cookie从你设置的时候,就会给个时间,不设置默认会话结束就过期; 淘宝购物 从你下单付款起,就会给这件货物设置一个收货期限时间,过了这个时间自动认为你收货(即订单结束); 闹钟 你设置的提醒时间,其实也就是它的过期时间; 再比如与您每天切身相关的产品需求,过完需求,你给出的上线时间,也就是这个需求的过期时间; 再通俗点讲,您今年的生日过完到明年生日之间也是相当于设置了有效期时间; !> 以上种种,我们能得出一个结论任何一件事、一个行为动作,都有一个时间、一个节点,甚至我们可以黑localStorage...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS关闭SELinux安全模块
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2整合Redis,开启缓存,提高访问速度