前言
前段时间做了一个合并音频的需求,当时用了crunker库快速完成了该需求,后面抽时间翻了下crunker源码,发现该库源码写的也非常清晰,主要是用Audio相关API完成的,简单易懂,本节我就来介绍一下crunker源码。
源码
crunker是利用音频API处理音频的库,有合并、导出、下载等功能,打开对应的github,找到src文件夹,crunker.ts文件就是各个方法的源码。
转换
fetchAudio 用于将音频文件转换为AudioBuffer;我们先明确AudioBuffer是什么?AudioBuffer就是内存中的音频资源,日常开发中我们拿到的音频一般是保存地址,说白了就是一段字符串,不可能进行二次操作,并且Web的Aduio API往往是对内存中的资源进行操作,所以我们先拿到音频资源。实现该方法分成了两步:
- 通过fetch请求文件地址,获取ArrayBuffer数据。
- new一个AudioContext,将ArrayBuffer转换成AudioBuffer。
步骤非常简单,但是我们最好了解用到的API,方便理解:
- fetch:一个异步获取资源的API,返回一个promise对象。
- AudioContext:音频上下文,可以处理音频,比如创建、解码等。
- AudioContext.decodeAudioData:AudioContext的一个方法,用来解码ArrayBuffer,返回AudioBuffer。
- ArrayBuffer:数据缓冲区,可以用来操作和存储二进制数据。
通过以上分析我们就能写出fetchAduio函数,这里我们就以单个录音地址为参数,源码中是接收录音地址数组为参数。
合并
mergeAudio可以用多个AudioBuffer数据合并成一个AduioBuffer,结合着fetchAudio它可以用合并多个音频文件,具体步骤如下:
- 使用createBuffer创建一个空白的AudioBuffer,用来存储合并后的AudioBuffer。
- 循环AudioBuffer数组,获取对应的音频通道数据。
- 循环音频通道数据,添加到空白AudioBuffer对应的通道中。 合并要复杂点,这里运用了两次循环,我们要清楚每次循环的目的是什么?在此之前我们还是要了解新的API:
- AudioContext.createBuffer:创建空白AudioBuffer对象。
- AudioBuffer.numberOfChannels:表示音频通道数,也就是我们说的声道,声道一般就是单声道(Mono)和立体声(Stereo),所以该值一般为1或者2。
- AudioBuffer.getChannelData:获取各个通道中的PCM数据。
注意,我们在创建AudioBuffer时传递的参数,比如声道数、时间必须是参数数组中的最大值,这样才能保证所有的音频数据被合并。
播放
play用来播放音频,接收一个AudioBuffer。播放的思路就很简单,创建音频源进行播放。用到的API如下:
- AudioContext.createBufferSource创建音频源,可以接收AudioBuffer来进行播放。
- connect:音频源方法,连接到目标节点。
- start:音频源方法,播放音频。
总结
以上方法理解起来并不困难,用到的API都是Audio API,我相信大部分人不会专门去MDN翻看这些API,通过学习源码我们就能理解掌握,下次碰到Audio相关就会有相关思路,甚至慢慢写出复杂逻辑。上述案例是用js重构了一下,源码用的是ts,感兴趣的可以继续探索。