博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
web-audio-api可视化音乐播放器,实现暂停切换歌曲功能,粉色系专场~
阅读量:6412 次
发布时间:2019-06-23

本文共 4797 字,大约阅读时间需要 15 分钟。

可视化的音乐播放器,可观看效果

了解Web-Audio-Api

  • 基础知识

<audio>标签是HTML5的新标签,通过添加src属性实现音乐播放。

AudioContext是音频播放环境,原理与canvas的绘制环境类似,都是需要创建环境上下文,通过上下文的调用相关的创建音频节点,控制音频流播放暂停操作等操作,这一些操作都需要发生在这个环境之中。

try{    var audioCtx = new (window.AudioContext || window.webkitAudioContext)(); }catch(e){    alert('Web Audio API is not supported in this browser');}复制代码

AudioNode接口是一个处理音频的通用模块,它可以是音频音源模块,音频播放设备模块,也可以是中间音频处理模块。不同的音频节点的连接(通过AudioContext.connect()),以及终点连接AudioContext.destination(可以看作是连接到耳机或扬声器设备)完成后,才能输出音乐。

常见的音频节点:AudioBufferSourceNode: 播放和处理音频数据AnalyserNode: 显示音频时间和频率数据 (通过分析频率数据可以绘制出波形图之类的视图,可视化的主要途径)GainNode: 音量节点,控制音频的总音量MediaElementAudioSourceNode: 关联HTMLMediaElement,播放和处理来自
  • 运行模式
  1. 创建音频上下文
  2. 在上下文中,创建音频源
  3. 创建音频节点,处理音频数据并连接
  4. 输出设备

创建音频上下文

try{    var audioCtx = new (window.AudioContext || window.webkitAudioContext)(); }catch(e){    alert('Web Audio API is not supported in this browser');}复制代码

创建音频源

由于音频文件的数据是二进制(非文本),所以要设置请求头的responseTypearraybuffer,将.mp3音频文件转换成数组缓冲区ArrayBuffer

AudioContext.decodeAudioData解码成功之后获取buffer,执行回调函数,将数据放入AudioBufferSourceNode

方法一采用流式加载音乐文件,简单易懂,缺点是通过createMediaElementSource加载的src文件必须是同源,不允许跨域

下面步骤主要根据方法2。

  • 方法一:通过HTMLMediaElement流式加载
复制代码
  • 方法二:通过XMLHttpRequest获取资源
let xhr = new XMLHttpRequest();    xhr.open('GET', '1.mp3', true);    xhr.responseType = 'arraybuffer';    xhr.onload = function () {      audioCtx.decodeAudioData(xhr.response, function (buffer) {        getBufferSuccess(buffer)      })    }复制代码
  • 方法三:通过input file获取
let input = document.querySelector('input');    input.addEventListener('change', function () {      if (this.files.length !== 0) {        let file = this.files[0];        let fr = new FileReader();        fr.onload = function () {          let fileRet = e.target.result;          audioCtx.decodeAudioData(fileRet, function (buffer) {            getBufferSuccess(buffer);          }, function (err) {            console.log(err)          })        }        fr.readAsArrayBuffer(file);      }    })复制代码

处理音频数据

function getBufferSuccess(buffer) {      // 创建频率分析节点      let analyser = audioCtx.createAnalyser();      // 确定频域的快速傅里叶变换大小      analyser.fftSize = 2048;      // 这个属性可以让最后一个分析帧的数据随时间使值之间的过渡更平滑。      analyser.smoothingTimeConstant = 0.6;      // 创建播放对象节点      let source = audioCtx.createBufferSource();      // 填充音频buffer数据      source.buffer = buffer;      // 创建音量节点(如果你需要用调整音量大小的话)      let gainNode = audioCtx.createGain();            // 连接节点对象      source.connect(gainNode);      gainNode.connect(analyser);      analyser.connect(audioCtx.destination);    }复制代码

获取音频频率

  • 方法一:用js的方法获取(通过监听audioprocess事件,由于性能问题,将会被弃用,不做详细说明,感兴趣的可以了解一下)
// 此方法需要补充节点的连接      let javascriptNode = audioCtx.createScriptProcessor(2048, 1, 1);      javascriptNode.connect(audioCtx.destination);      analyser.connect(javascriptNode);              this.javascriptNode.onaudioprocess = function () {            currData = new Uint8Array(analyser.frequencyBinCount);            analyser.getByteFrequencyData(currData);        }复制代码
  • 方法二:用AnalyserNode获取

获取AnalyserNode节点里的频率长度frequencyBinCount,实例化长度为8位的整型数组,通过AnalyserNode.getByteFrequencyData将节点中的频率数据拷贝到数组中去,值的大小在0 - 256之间,数值越高表明频率越高;AnalyserNode.getByteTimeDomainData原理一样,不过获取的是频率大小,两种方法根据需求选一种即可。

function getData () {      // analyser.frequencyBinCount 可视化值的数量,是前面fftSize的一半      let currData = new Uint8Array(analyser.frequencyBinCount);      analyser.getByteFrequencyData(currData);      analyser.getByteTimeDomainData(currData);    }复制代码

输出设备

AudioBufferSourceNode.start(n) n表示开始的时间,默认为0,开始播放音频 AudioBufferSourceNode.stop(n) 音频在第n秒时间停止,若没有传值表示立即停止

其他api

AudioContext.resume() 控制音频的播放

AudioContext.suspend() 控制音频的暂停
AudioContext.currentTime 获取当前音频播放时间
AudioBufferSourceNode.buffer.duration 获取音频的播放总时长
GainNode.gain.value 控制音量大小 [0, 1]
GainNode.gain.linearRampToValueAtTime 实现音量的渐入渐出

Canvas绘制可视化效果

了解上面的api,就可以来着手绘制啦~,你想绘啥就绘啥,频繁的调用canvas的api很耗性能问题,这里讲下我在测试中提高性能的小技巧。

  • 多分层canvas,一些不需要频繁改动的绘制,例如背景,固定的装饰绘制,可以采用另一个canvas的上下文来绘制
  • 离屏绘制,原理是生成一个没有出现在页面的canvas,在这个缓存的canvas中绘制,而真正展示的canvas只需要通过drawImage这个api将画面绘制出来即可,
  • 固定好lineWidth的长度,而不是每绘制一个就设定一次lineWidth
  • 绘制区域提前计算好,不要让canvas边绘制同时还要计算位置(canvas:好累哦~) 总而言之,少调用canvas api,可是也不要为了提高性能而抛弃你的一些天马星空的想法哦

遇到的问题

在切换歌曲中,遇到了这个报错Failed to set the 'buffer' property on 'AudioBufferSourceNode': Cannot set buffer to non-null after it has been already been set to a non-null buffer at AudioContext,大致是讲AudioBufferSourceNode的buffer属性在之前我已经设置过了,不能被重新设置新的buffer值,由于播放歌曲主要是通过其数组缓冲区ArrayBuffer来进行,可看看,解决办法就是当需要切换歌曲情况下,将当前的AudioBufferSourceNode销毁,重新创建上下文环境,音频节点,连接等操作。

,交互部分写得有点乱,因为当时原来只是想练练可视化,之后想到啥功能就加,所以导致代码看起来冗余繁琐,大家可以参考看看audio实现,主要在MusicPlay对象。

小白第一次发表博文,发现写博文比写一个demo还要时间长,怕写出来的东西有错误会误导大家(有错误请大家评论指出~),所以会去查很多相关资料,这个过程也是学习的过程,以后会经常写写博文滴!最后,希望大家通过这篇文章也能学会自己做这种可视化的效果,配合一些可视化库还能做出很酷炫的效果呢,一起互相学习进步吧,加油!(。・д・。)

转载地址:http://nzura.baihongyu.com/

你可能感兴趣的文章
08/23 学习总结
查看>>
物理层
查看>>
linux多网卡路由设置
查看>>
八大监听器
查看>>
self.navigationController退出到指定页面,或者一次性pop出n个页面
查看>>
iptables 端口转发以及双向通信
查看>>
备战一线互联网公司Java工程师面试题 (1)
查看>>
jquery图片切换插件jquery.cycle.js参数详解
查看>>
JavaScript push() 方法
查看>>
Map集合
查看>>
JSP基础语法1
查看>>
elasticsearch Java API 之GET API & DELETE API
查看>>
《深入理解Java虚拟机》——GC基础概念
查看>>
微信小程序联盟:官方文档+精品教程+demo集合(5月31日更新,持续更新中……)...
查看>>
Fastjson 的 Set类型和 WriteClassName 选项引起的BUG
查看>>
翻译: 星球生成 II
查看>>
IOS 多线程
查看>>
python序列化数据本地存放
查看>>
#CCNA#IP地址、子网划分参考资料网址
查看>>
比较不错的图片上传插件
查看>>