原文出处:http://mindtherobot.com/blog/555/android-audio-problems-hidden-limitations-and-opensl-es/
我最近一直在研究Android音频API。之前我写过一篇介绍性的文章来说明Android上可用的三套API。现在我假设你在看这篇文章的时候已经对AudioTrack,SoundPool和MediaPlayer有过基本的了解了。
这篇文章介绍了一些我使用Android现有音频API的一些经验,包括一些问题和我遇到过的困难。后面还有一小部分OpenSL ES的内容,OpenSL ES是一种将会在Android后续发布版本中得到支持的音频标准(译注:2.3已经发布了此项支持)。
现有API中的一些已知问题
目前所有这3套已有API(SoundPool,AudioTrack和MediaPlayer)中都存在着bug(和文档中不一致的行为),隐藏的(文档中没有的)限制和一些设计问题。我们来看看各个API中这些问题的体现。
AudioTrack
AudioTrack是最灵活、高级的音频API,用它可以实现将原始音频数据直接送到硬件的操作。不过虽然它确实能在一定程度上让你解决某些实际问题,但它还是在某些非常基本的情况下显得不是很给力。
我个人遇到过以下这些问题,而后来我发现他们已经被Piotr Buła作为bug提交给Android官方了。不幸的是,直到现在还没有一个问题得到了解决。(译注:不知现在是不是解决了:))
Issue 3197: 使用AudioTrack静态模式(static mode)尝试快速重复播放声音后会导致音频播放中断。
这个bug实际上会导致开发人员不能用AudioTrack来实现软件合成器或游戏中的音频播放,而这种方式所能带来的低延迟和播放过长音频的好处,恰是你用SoundPool所不能实现的。
在我来看,这是个捉襟见肘的问题。如果你加入一些延迟(Droid下用SystemClock.sleep()歇30-40ms),那这个问题就能解决了。当然,这是我不推荐的恶心解决方法。所以就认为这是个不可行的解决方法吧。
Issue 3198: AudioTrack 播放状态(playback state)和真实情况不一致
这个问题现在已经不怎么多提了,但这个问题好像是这样:当采样播放完成后,AudioTrack中的代码没有正确更新其状态。
也许你已经可以想到这2个问题会造成多严重的影响,AudioTrack基本来说不是非常稳定,或者说没有被很好的实现。所以如果你打算在你的应用中使用它,最好在开始前进行一下用例测试。下面继续了解一下你可能会考虑做备选方案的其他API的限制和问题。
SoundPool
SoundPool是在Android上播放简短且低延迟音频的基础。大部分需要使用音效的应用、游戏普遍都会使用这个类。然而,它的功能很有限,而且文档中缺少相关限制以及它支持的用例的介绍。
虽然Piotr Bula也上报过一个SoundPool的问题,不过这问题并不严重( 和release()有关且我这好像无法重现)。我还遇到了一些其它问题:
如你所见,这些问题和在一起已经能给对SoundPool功能抱有期望的开发人员带来足够的痛苦了。再加上上面提到的AudioTrack的问题,在Android上实现低延迟音频播放就成了一个非常头疼的问题,这个系统的这部分功能相比iPhone来说差距还是很大的。我们必须承认这些问题并且以社区形式来解决这些问题。这就是为什么我请求你们做你们能做的最简答的事:如果这些问题影响了你当前或未来的应用开发,就去bug页的下面给这些bug投个票。
MediaPlayer
MediaPlayer可能是Android上应用最广泛的音频API了,而且它好像也是功能实现最好的。虽然在开发讨论组(dev groups)上能听听到各种关于MediaPlayer的抱怨声,但我遇到的最糟糕的事就是那些随机的日志信息和不完善的异常处理。
另外还有许多人希望能够实现从InputStream播放的功能。由于大部分MediaPlayer的代码都是原生的(native),这个功能可能有点难实现,但也不是完全没可能。
原生音频API和OpenSL ES
随着每个Android系统的发布,对2D和3D快速原生图形绘制的支持在一直不断的得到改进,而且似乎已经达到了能够满足大部分应用程序需求的程度。音频部分的情况却是大相径庭,实在是很遗憾。毕竟,音频相关软件一直是桌面电脑的一个大盘子,但像便携性这种移动特性对音频软件作者来说可能还是很有吸引力的。
然而,关于音频软件需要高性能、高品质、低延迟的实现这些需求的建议基本都是需要原生代码(或派生出的API),可直到Froyo我们都还不能那么做。
我听到消息说未来的系统版本可能会支持OpenSL ES,一个由OpenGL组织制定的不是很有名的标准,技术实现也很类似OpenGL。
读过相关标准文档后,我列出了一下一些评价:
1.某些OpenSL ES的功能特性不是非常的需要,比如对MIDI的支持。
2.不是很清楚Android会支持哪个profile(译注:类似OpenGL ES cm或cl的API版本标准),如果你问我,我只能说哪个都不合适。
3.实现的主要功能特性都已经被其他已有API包括了,是的,包括他们的问题,但为什么就不能把现有API的问题修正反而却要加入新的呢?
4.没有提高性能,降低延迟等等。这些需求的实现程度都是依赖于标准实现的好坏程度。
5.如果系统实现这个新API的做法是用现有的原生代码的话,那么我们还是会遇到那些同样的问题,只不过是旧瓶装新酒。
因此,你可能已经注意到了,我非常不确定OpenSL ES是否能够解决我们现在遇到的这些音频问题。我觉得还是应该花些时间在修正已有API上。一旦现有API完善了,我们能在确实需要的时候,基于这些API实现OpenSL ES。但现在,我还是倾向于用原生代码实现类似AudioTrack的功能,最好能是已经解决了那些问题的AudioTrack。
友情链接:http://blog.chinaunix.net/uid/9185047/abstract/10.html
OpenSL ES - 嵌入式音频加速标准
OpenSL ES™ 是无授权费、跨平台、针对嵌入式系统精心优化的硬件音频加速API。它为嵌入式移动多媒体设备上的本地应用程序开发者提供标准化, 高性能,低响应时间的音频功能实现方法,并实现软/硬件音频性能的直接跨平台部署,降低执行难度,促进高级音频市场的发展。
同样的数据,同样直接在jni层调用,audiotrack要更多的cpu处理,一有其他高运算程序执行,播放就不连贯,会出现杂音。用OpenSLES就不会有这样的问题,声音听起来舒服很多。
用 OpenSLES播放声音,主要是设置PCM数据格式,其代码如下:
SLDataFormat_PCM pcm;
pcm.formatType = SL_DATAFORMAT_PCM;
pcm.numChannels = 2;//跟下面的channelMask 要配对 不会会报错
pcm.samplesPerSec = pAD->sample_rate*1000;
pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
pcm.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; //立体声 这是参照audiotrack CHANNEL_OUT_STEREO = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT)得到的
pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
bitsPerSample ndk提供的例子是SL_PCMSAMPLEFORMAT_FIXED_8的 要根据实际情况修改,不然会就不能听到实际声音了,只会听到一片杂音。