- 更新于 2019-08-11
去年 (2018),因为对 JavaScript 比较感兴趣,加上学校在教前端的内容,所以做了一个简单的音乐可视化网页。然而因为没有系统的学过 JS,所以编码时候遇到了内存泄露还有一些奇怪的问题没有解决,直到今天才把文章给补完。
很多年以前,还在用着诺基亚塞班手机的时候,我非常喜欢一款播放器,名字叫 LCG Jukebox ,界面大概长这样
右上角会有一个简单的波形分析器,波形会根据音乐的内容而变化。所以想在网页播放音乐时也能展示出类似的效果。查阅一些资料之后,发现了一个名为 p5.js 的 JavaScript库,使用 p5.js 可以方便地处理视音频和绘图,其中 p5 的音频库能很好地实现波形效果,p5.js 的官网提供了一个很好的范例 Oscillator Frequency 要实现音乐文件作为音频输入,通过 loadSound()
let music
function preload() {
music = loadSound("path_to_music_file")
}
function setup() {
music.play()
}
有了音频源之后,使用 p5.FFT 提供的 waveform() 方法分析出波形数据,然后在屏幕上画出波形动画:
let fft
function setup() {
let cnv = createCanvas(150, 50)
noFill()
cnv.parent('vz')
fft = new p5.FFT()
frameRate(30)
}
function draw() {
let waveform = fft.waveform()
// white line
stroke(255, 255, 255)
strokeWeight(2)
beginShape()
for (let i = 0; i < waveform.length; i++) {
let x = map(i, 0, waveform.length, 0, width)
let y = map(waveform[i], -1, 1, 0, height)
vertex(x, y)
}
endShape()
}
到这里基本就完成了。由于网页上有多个音乐文件,加上没有暂停音乐的功能,于是优化一下,在点击波形动画区域来实现播放/暂停,这里需要引入 p5.dom 这个库:
function setup() {
let cnv = createCanvas(150, 50)
// ...
cnv.mouseClicked(togglePlay)
// ...
}
function togglePlay() {
if (song.isPlaying()) {
song.pause()
} else {
song.play()
}
}
在音乐正在播放时,切换到下一首,有时即使利用 song.stop()
也会出现同时播放两首音乐的问题。这个问题困扰了我很久,后来仔细阅读文档,发现是音频默认的 sustain
播放模式造成的,修改为 restart
模式即可解决:
const music = [loadSound("music_file1"), loadSound("music_file2"),
loadSound("music_file3")]
function play_music(id) {
let next = music[id]
if (song != next) {
song.stop()
song = next
}
song.playMode('restart')
song.play()
}
为网页所写的代码在这里 andytimes / web2018 ,然后访问 2018.andytimes.xyz 能看到最终效果,网页只是歌手 Alan Walker 官网 的仿制版,其中还擅自使用了一些图片和音频素材:
在利用 p5.js 绘图的页面,内存占用挺高的,而且似乎还存在着内存泄露的问题,我也找过别的解决方案,比如这种更原生一些的方法: 基于Web Audio API实现音频可视化效果 ,然而内存泄露的问题更加严重,也许是我的水平不够吧 hhh,希望以后还有机会继续了解更多 Web端的知识。