背景
最近在做微信小游戏时,需要实现一个讲音乐图形化的需求,网上很多用Web Audio API就可以轻松实现把音频转换为图形的例子,比如Web Audio API 绘制可视化音乐这个,但微信小游戏环境目前还不支持Web Audio API,所以想到了办法只能是做离线分析音频的频率了。
那离线要怎么做?找个真实的浏览器环境再写段 JavasScript 脚本去采集?不不不,感觉实现起来就很麻烦,但思路基本是正确的,只是我们不需要用真实的浏览器去解析,大 google 开源了一个headless Chrome的工具Puppeteer,这样我们就可以集成到 nodejs 中,写出基于 Nodejs + Puppeteer的离线解析音频频率的工具。
Puppeteer
首先创建一个能提供AudioContext浏览器环境,并打开一个空白页面:
1 | const browser = await puppeteer.launch({ |
解析音频频率信息的代码是在浏览器中执行的,所以需要用到page.evaluate:
1 | // nodejs环境 |
在这里我们的已经成功创建audioContext上下文了,剩下的就是怎样解析音频的事情了。
加载音频
首先作为一个工具来说,我们的音频基本是放在本地的,所以需要用 nodejs 的readFile来读取本地文件,这里又抛出了另外一个问题,在 Nodejs 环境下,怎么样把读取的音频文件给到浏览器环境呢?别担心,Puppeteer 已经提供了exposeFunction来处理这种特殊的交互了,首先我们在 nodejs 环境下,定义一个用来加载音频文件函数loadFile,的,注意文件格式选择base64文本格式,buffer格式是无法传递给浏览器环境的:
1 | await page.exposeFunction('loadFile', (filePath) => { |
然后我们在浏览器新建一个函数decode,用来解码 nodejs 传递过来的音频文本信息,最后返回一个AudioBuffer:
1 | const decode = () => { |
然后再创建一个音频bufferSource,并把解码后的AudioBuffer添加到 bufferSource:
1 | const audioFile = await loadFile('./audio/bgm.mp3'); |
采集音频频率数据
首先我们需要创建一个analyserNode,用来处理获取音频频率数据:

1 | const analyserNode = audioContext.createAnalyser(); |
然后跑一下代码,这时候已经可以正常听到 bgm 响起了。等等,好像有点不对,难道我要等一首 bgm 全部播完才算采集完吗?没错,AudioContext确实只能是实时采集音频数据。但是好在Web Audio提供了OfflineContext,我们可以在设备不播放音乐的时候,同样采集到数据。我们只有稍微修改一下代码,首先创建一个offlineContext上下文:
1 | const offlineContext = new OfflineAudioContext( |
然后新增一个scriptProcessorNode,用来实时分析音频信息:
1 | const scriptProcessorNode = offlineContext.createScriptProcessor(); |
最后我们的采集流程是这样的:

也就是 analyserNode 与输出音频直接新增了一个scriptProcessorNode模块,同时该模块注册一个回调方法去采集过程中的音频数据:
1 | const analyseData = {}; |
然后再注册一个oncomplete方法,把采集到的数据通过 exposeFunction 暴露到浏览器环境的方法writeFile输出到 json 文件。
最后再播放并渲染音频:
1 | bufferSource.start(); |
最后
很多程序语言都能解析音频的频率信息,包括纯 nodejs、python 都可以,本文只是用 Puppeteer 这个无界面浏览器去做音频解析,最后我把以上的处理方案封装成一个命令工具:audio-analyser-cli,感兴趣可以去我的 github 了解:https://github.com/inarol/audio-analyser-cli
[本文谢绝转载,谢谢]