Android深入浅出之Audio
第一部分 AudioTrack分析
一 目的
本文的目的是通过从Audio系统来分析Android的代码,包括Android自定义的那套机制和一些常见类的使用,比如Thread,MemoryBase等。
分析的流程是:
l 先从API层对应的某个类开始,用户层先要有一个简单的使用流程。
l 根据这个流程,一步步进入到JNI,服务层。在此过程中,碰到不熟悉或者第一次见到的类或者方法,都会解释。也就是深度优先的方法。
1.1 分析工具
分析工具很简单,就是sourceinsight和android的API doc文档。当然还得有android的源代码。我这里是基于froyo的源码。
注意,froyo源码太多了,不要一股脑的加入到sourceinsight中,只要把framwork目录下的源码加进去就可以了,后续如要用的话,再加别的目录。
二 Audio系统
先看看Audio里边有哪些东西?通过Android的SDK文档,发现主要有三个:
l AudioManager:这个主要是用来管理Audio系统的
l AudioTrack:这个主要是用来播放声音的
l AudioRecord:这个主要是用来录音的
其中AudioManager的理解需要考虑整个系统上声音的策略问题,例如来电话铃声,短信铃声等,主要是策略上的问题。一般看来,最简单的就是播放声音了。所以我们打算从AudioTrack开始分析。
三 AudioTrack(JAVA层)
JAVA的AudioTrack类的代码在:
framework\base\media\java\android\media\AudioTrack.java中。
3.1 AudioTrack API的使用例子
先看看使用例子,然后跟进去分析。至于AudioTrack的其他使用方法和说明,需要大家自己去看API文档了。
//根据采样率,采样精度,单双声道来得到frame的大小。 int bufsize = AudioTrack.getMinBufferSize(8000,//每秒8K个点 AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道 AudioFormat.ENCODING_PCM_16BIT);//一个采样点16比特-2个字节 //注意,按照数字音频的知识,这个算出来的是一秒钟buffer的大小。 //创建AudioTrack AudioTrack trackplayer = new AudioTrack(AudioManager.STREAM_MUSIC, 8000, AudioFormat.CHANNEL_CONFIGURATION_ STEREO, AudioFormat.ENCODING_PCM_16BIT, bufsize, AudioTrack.MODE_STREAM);// trackplayer.play() ;//开始 trackplayer.write(bytes_pkg, 0, bytes_pkg.length) ;//往track中写数据 …. trackplayer.stop();//停止播放 trackplayer.release();//释放底层资源。
这里需要解释下两个东西:
1 AudioTrack.MODE_STREAM的意思:
AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。
这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。
而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。
这种方法对于铃声等内存占用较小,延时要求较高的声音来说很适用。
2 StreamType
这个在构造AudioTrack的第一个参数中使用。这个参数和Android中的AudioManager有关系,涉及到手机上的音频管理策略。
Android将系统的声音分为以下几类常见的(未写全):
l STREAM_ALARM:警告声
l STREAM_MUSCI:音乐声,例如music等
l STREAM_RING:铃声
l STREAM_SYSTEM:系统声音
l STREAM_VOCIE_CALL:电话声音
为什么要分这么多呢?以前在台式机上开发的时候很少知道有这么多的声音类型,不过仔细思考下,发现这样做是有道理的。例如你在听music的时候接到电话,这个时候music播放肯定会停止,此时你只能听到电话,如果你调节音量的话,这个调节肯定只对电话起作用。当电话打完了,再回到music,你肯定不用再调节音量了。
其实系统将这几种声音的数据分开管理,所以,这个参数对AudioTrack来说,它的含义就是告诉系统,我现在想使用的是哪种类型的声音,这样系统就可以对应管理他们了。
3.2 分析之getMinBufferSize
AudioTrack的例子就几个函数。先看看第一个函数:
AudioTrack.getMinBufferSize(8000,//每秒8K个点 AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道 AudioFormat.ENCODING_PCM_16BIT); ----->AudioTrack.JAVA
//注意,这是个static函数
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) { int channelCount = 0; switch(channelConfig) { case AudioFormat.CHANNEL_OUT_MONO: case AudioFormat.CHANNEL_CONFIGURATION_MONO: channelCount = 1; break; case AudioFormat.CHANNEL_OUT_STEREO: case AudioFormat.CHANNEL_CONFIGURATION_STEREO: channelCount = 2;--->看到了吧,外面名字搞得这么酷,其实就是指声道数 break; default: loge("getMinBufferSize(): Invalid channel configuration."); return AudioTrack.ERROR_BAD_VALUE; } //目前只支持PCM8和PCM16精度的音频 if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT) && (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) { loge("getMinBufferSize(): Invalid audio format."); return AudioTrack.ERROR_BAD_VALUE; } //ft,对采样频率也有要求,太低或太高都不行,人耳分辨率在20HZ到40KHZ之间 if ( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) ) { loge("getMinBufferSize(): " + sampleRateInHz +"Hz is not a supported sample rate."); return AudioTrack.ERROR_BAD_VALUE; } //调用native函数,够烦的,什么事情都搞到JNI层去。 int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat); if ((size == -1) || (size == 0)) { loge("getMinBufferSize(): error querying hardware"); return AudioTrack.ERROR; } else { return size; }
native_get_min_buff_size--->在framework/base/core/jni/android_media_track.cpp中实现。(不了解JNI的一定要学习下,否则只能在JAVA层搞,太狭隘了。)最终对应到函数
1 static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz, 2 3 jint sampleRateInHertz, jint nbChannels, jint audioFormat) 4 5 {//注意我们传入的参数是: 6 7 //sampleRateInHertz = 8000 8 9 //nbChannels = 2; 10 11 //audioFormat = AudioFormat.ENCODING_PCM_16BIT 12 13 int afSamplingRate; 14 15 int afFrameCount; 16 17 uint32_t afLatency; 18 19 //下面涉及到AudioSystem,这里先不解释了, 20 21 //反正知道从AudioSystem那查询了一些信息 22 23 if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) { 24 25 return -1; 26 27 } 28 29 if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) { 30 31 return -1; 32 33 } 34 35 36 37 if (AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) { 38 39 return -1; 40 41 } 42 43 //音频中最常见的是frame这个单位,什么意思?经过多方查找,最后还是在ALSA的wiki中 44 45 //找到解释了。一个frame就是1个采样点的字节数*声道。为啥搞个frame出来?因为对于多//声道的话,用1个采样点的字节数表示不全,因为播放的时候肯定是多个声道的数据都要播出来//才行。所以为了方便,就说1秒钟有多少个frame,这样就能抛开声道数,把意思表示全了。 46 47 // Ensure that buffer depth covers at least audio hardware latency 48 49 uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSamplingRate); 50 51 if (minBufCount < 2) minBufCount = 2; 52 53 uint32_t minFrameCount = 54 55 (afFrameCount*sampleRateInHertz*minBufCount)/afSamplingRate; 56 57 //下面根据最小的framecount计算最小的buffersize 58 59 int minBuffSize = minFrameCount 60 61 * (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1) 62 63 * nbChannels; 64 65 return minBuffSize; 66 67 }