音频播放器的制作与WebAudio API

前言

最近一直在制作一个音乐web app的练习项目,播放器的功能当然是音乐类app的基础;除此之外,个人对于音乐可视化也比较感兴趣,一旦遇上音乐就想着可视化;然而这些功能都离不开HTML5推出的audio元素以及强大的WebAudio API

audio元素

audio元素是H5新增的多媒体标签,通过它就可以轻松的用来进行音频播放等一些操作;

常用属性

  1. src:音频地址;

  2. autoplay:布尔属性,默认值为false;当设置时,音频会马上播放,而非等到音频加载完后再播放;

  3. loop:布尔属性,默认值为false;设置时音频播放完后会自动循环播放

  4. controls:布尔属性,默认值为false;设置时浏览器会给audio元素提供一个默认的控制面板(UI,可以进行播放,暂停等操作);

  5. preload:用来提示浏览器以什么方式来加载音频信息,有autononemetadata三个选项,当设置值为空字符串时等同于auto没有规定的默认值,所以不同浏览器的默认值可能不一样;autoplay属性的优先级大于preload

    • none:示意用户可能不会播放该音频,或者服务器希望节省带宽;换句话说,该音频不会被缓存
    • metadata:示意即使用户可能不会播放该音频,但获取元数据 (例如音频长度) 还是有必要的。
    • auto:示意用户可能会播放音频;换句话说,如果有必要,整个音频都将被加载,即使用户不期望使用。
  6. muted:布尔属性,默认为false;设置时代表音频是静音的;

常用的DOM属性

  1. paused:音频元素是否处于暂停状态(trueorfalse),缓冲状态并不算暂停状态

  2. currentTime:音频元素的当前播放时间,单位为秒(浮点型);设置该属性可以调整音频的当前播放时间!

  3. duration:音频元素的总时间长度,单位为秒;没有音频文件时,值为0

常用方法

  1. play():播放音频;
  2. pause():暂停播放;

播放器的制作

虽说audio元素可以使用自带的控制器进行操作,但是一般为了融合进产品,当然会重新设计;所以一般是隐藏audio元素,利用js获取其DOM,然后使用自带的方法和属性进行控制;

  1. 播放/暂停/停止:这些利用play()pause()方法即可实现;

  2. 进度条显示:利用currentTimeduration属性即可获知当前播放进度的百分比;

  3. 进度跳转:点击进度条元素时获取其event.offsetX及其容器宽度即可知道点击位置对应的进度百分比,然后设置currentTime即可;

1
2
3
function timePoint(e) { // 歌曲进度条跳转
$player.currentTime = $player.duration * e.offsetX / barWidth
}
  1. 上一首/下一首/其它切换操作:直接设置src即可;

音频可视化

音频可视化一般是获取音频频率进行相应转换,形成与音乐同步的视觉信息;而要获取频率信息就需要借助WebAudio API的方法了。

mark

上面是来自MDN的一张图,很好地表达了Audio contextAudio node之间的关系,其中Inputs就是音频文件或信息,而Destination则是播放设备;因此,一个Audio context的作用就是引入音源,连接中间节点(AudioNode,可以多个进行串联,有不同类型的AudioNode),最后输出到播放设备;

获取频率信息

  1. 创建上下文
1
2
let AudioContext = window.AudioContext || window.webkitAudioContext
let ctx = new AudioContext()
  1. 创建音源
1
let source = ctx.createMediaElementSource(this.$player)

createMediaElementSource只是创建音源的其中一种方法,该方法接受一个audio元素或video元素作为音源对象,得到一个MediaElementAudioSourceNode对象(属于AudioNode节点);

  1. 创建分析器节点
1
let analyser = ctx.createAnalyser()

createAnalyser方法可以创建一个AnalyserNode,而AnalyserNode可以实时获取音频的频率相关信息(前提是连接了音源节点);

  1. 连接音源、分析器和播放设备
1
2
source.connect(analyser)
analyser.connect(ctx.destination)

将音源与分析器连接后,分析器就可以工作了;destination属性是默认的音频渲染设备,连接destination后就能正常输出音频信息;

以上就是获取音频频率信息的最直接方法,其实可以在音源和destination之间添加多个不同的AudioNode,玩出更多的花样。

AnalyserNode

分析器节点拥有丰富的频率信息,是可视化的主要信息来源;常用的属性和方法有:

  1. fftSize:音频频域的FFT样本窗口大小;

fftSize 属性的值必须是从32到32768范围内的2的非零幂; 其默认值为2048.

  1. frequencyBinCount:数值为fftSize属性值的一半,代表可用于可视化的频率信息数量;

  2. getByteFrequencyData(arr):获取当前频率信息至Uint8Array类型数组中;Uint8Array类型数组中元素值为0~255之间的整数;

  3. getFloatFrequencyData(arr):获取当前频率信息至Float32Array类型数组中;Float32Array类型数组中元素值为32位浮点数,因此方法获取的数据具有更高的精度;

可视化

当成功创建音源和分析器并进行连接后,可以利用AnalyserNode的方法和属性获取实时频率信息,进而进行绘制:

1
2
let arr = new Uint8Array(analyser.frequencyBinCount)
analyser.getByteFrequencyData(arr)

以上步骤就能获取实时的音频频率数据了,只需在requestAnimationFrame中不停地利用getByteFrequencyData等方法获取实时数据就能实现各种各样的音乐可视化,数据有了,后面就靠创意了。

移动端兼容性问题

目前使用过程中发现,在移动端的某些浏览器使用getByteFrequencyData获取数据时会出现数据全为0的情况,找了很久也没找到解决办法,可能是某些浏览器内核对于WebAudio API的支持程度不够吧。

参考文档

  1. audio | MDN
  2. HTMLMediaElement | MDN
  3. AudioContext | MDN
  4. AnalyserNode | MDN
  5. 媒体相关事件 | MDN