Android混音播放(序)

1,478 阅读3分钟

1. 音频播放Api

安卓sdk里,播放音频有 SoundPool,MediaPlayer, AudioTrack 三种方案。

  1. SoundPool,明显不适合技术选型,因为它比较适合播放短促音效,文件小的音频。

  2. MediaPlayer,使用频繁的方案,自带解码,支持mp3,wav等音频文件,但只支持单一音频播放。同样不适合。

  3. AudioTrack,偏底层的音频播放方案,只支持pcm文件。所以,需要将音频文件解码成PCM(byte[] ),再将数据读取到固定的buffer缓存块里,然后再写入 AudioTrack ,就可以播放声音。

image2022-4-9_23-18-13.png

由于前面两个选项已被否决。那么,AudioTrack适不适合混和播放呢?暂不做确认,先看看什么是混和播放。

2. 混音播放

混音播放,也就是多音轨同时播放,暂停,seekTo 等操作。


其实MediaPlayer 也不是不行。新建多个MediaPlayer实例,然后同时操作,该方案其实也是项目里Loop 最初的实现方案,曲谱的实现方案。

image2022-4-9_21-22-3.png

虽然项目里的重点模块-曲谱都用上该方案,说明可行性还是有的。但终究不是终极方案。

比如所有音轨播放之前,播放一段前置节拍。此时就会出现衔接不上的听感。


由于MediaPlayer的实现是对AudioTrack的封装。所以,这个方案的逻辑,其实也是系统针对多应用同时播放声音的方案。

image2022-4-9_22-36-10.png

所以,简单的将MediaPlayer替换成AudioTrack,通过多个AudioTrack实例来实现,原理上是一模一样的。


那还有没有其他方案呢。

这里就不得不探讨下 声音的数字化的原理:在单位时间内对声波进行采样,最后量化成pcm数据。

image2022-4-9_22-53-7.png

同理,混音的原理是是:各个声音波形的叠加,也就是pcm数据的叠加。

而安卓sdk提供的音频API里,唯一能够针对pcm数据进行播放的,就只有AudioTrack。


结合上述的分析,具体的方案如下图:

image2022-4-9_23-37-29.png

所以,该方案可行?

这里抛开其他一些额外处理(比如不同采样率的音频混音,混音后的溢出),理论上是可行的,但其实有个致命的问题。

那就是多音轨时,jvm内存可能会爆掉。


这笔帐是可以算清楚的,

假如采样率为 44100HZ,位深为 16bit,声道数为 2 的 pcm,60 秒大小应为

44100×16bit×2×60s÷8bit=10,584,000Byte=10.0936889648MB 

而 Loop 单轨最高录制 5分钟,即单轨大小为 50M,8轨共计 400M

对于一些低端机型,怕是要承受不来的。


不过实际场景并没有这么极限。所以,应该是可行的,网上也有相应的例子。

实际上,恩雅APP采用了Google Oboe的方案,比AudioTrack更加底层,更加高效以及低延迟。

但混音原理是一样的,至于不同采样率的音频混音,混音后的溢出同样是存在的,这些还未完善。

而内存溢出方面,由于JNI 没有内存限制,所以比AudioTrack更加放心。实测录制8条5分钟的音轨,整个应用占用的将近1G,1+3机型也不会出现内存溢出的问题。

至于为何一开始没有在AudioTrack下功夫,只是项目前期已经接入Google Oboe,也就不再考虑范围。

所以,接下来,则是重头戏 Oboe 的混音实现了。由于篇幅太长,只能下集解说了。