网上已有许多朋友对Android音频子系统做了透彻的分析,我这完全是给自己在做学习笔记本文基于Android Ics

AudioTrack的使用实例,在google的源码中已经为我们准备了AudioTrack的测试代码,代码路径如下:

frameworks/base/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioTrackTest.java

源码太长了鸭梨太大了,我随便在该测试代码中抽取了几个API方法,以至于了解AudioTrack是怎么使用的,方便后面的分析。

好吧来看看testSetStereoVolumeMax这个API吧从字面意思理解应该是设置音量了,具体的实现看下面源码

//Test case 1: setStereoVolume() with max volume returns SUCCESS @LargeTest public void testSetStereoVolumeMax() throws Exception { // constants for test final String TEST_NAME = "testSetStereoVolumeMax"; final int TEST_SR = 22050; final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO; final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT; final int TEST_MODE = AudioTrack.MODE_STREAM; final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC; //-------- initialization -------------- int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT); AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT, minBuffSize, TEST_MODE); byte data[] = new byte[minBuffSize/2]; //-------- test -------------- track.write(data, 0, data.length); track.write(data, 0, data.length); track.play(); float maxVol = AudioTrack.getMaxVolume(); assertTrue(TEST_NAME, track.setStereoVolume(maxVol, maxVol) == AudioTrack.SUCCESS); //-------- tear down -------------- track.release(); }


从上面的代码我们可以知道当我们使用AudioTrack的时候第一步要做的事情就是初始化AudioTrack,初始化分两部走

1)调用getMinBufferSize get the minimum buffer size required for the successful creation of an AudioTrack

2)创建AudioTrack实例

3)用AudioTrack中的各种API去做各种事,比如说上面的track.write(data, 0, data.length)将数据写入硬件,track.play()播放音频数据

我沿着这几个艰辛的目标回到了AudioTrack这个类的实现frameworks/base/media/java/android/media/AudioTrack.java

getMinBufferSize究竟是怎么实现的,在分析它的源码之前我们需要对它的那三个参数有一定的了解
@param sampleRateInHz:采样率

------------人耳能听到大概是20Hz40000Hz吧
@param channelConfig:声道

------------CHANNEL_OUT_MONO
------------CHANNEL_CONFIGURATION_MONO

------------CHANNEL_OUT_STEREO

------------CHANNEL_CONFIGURATION_STEREO

@paramaudioFormat:音频数据格式,每个采样点所占的字节数

------------ENCODING_PCM_16BIT/*两个字节*/

------------ENCODING_PCM_8BIT/*一个字节*/

具体的还请看源代码

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:/*Double track */ channelCount = 2; break; default: loge("getMinBufferSize(): Invalid channel configuration."); return AudioTrack.ERROR_BAD_VALUE; } /*Each sample point how many bytes */ if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT) && (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) { loge("getMinBufferSize(): Invalid audio format."); return AudioTrack.ERROR_BAD_VALUE; } if ( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) ) { loge("getMinBufferSize(): " + sampleRateInHz +"Hz is not a supported sample rate."); return AudioTrack.ERROR_BAD_VALUE; } 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; } }

最后到哪去了,最后又转向JNI去了
getMinBufferSize方法在对三个参数解析完后开始通过JNI调用native_get_min_buff_size本地方法去具体的申请这个最小的buffer

再回到这个本地方法的实现部分,native_get_min_buff_size本地方法的实现代码路径位于framework/base/core/jni/android_media_track.cpp

static JNINativeMethod gMethods[] = { // name, signature, funcPtr {"native_get_min_buff_size", "(III)I", (void *)android_media_AudioTrack_get_min_buff_size}, };

由上面这个方法列表再具体定位到(void *)android_media_AudioTrack_get_min_buff_size,本地方法中去,代码如下

/*  *returns the minimum required size for the successful creation of a streaming AudioTrack  *returns -1 if there was an error querying the hardware.*/static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env,  jobject thiz,    jint sampleRateInHertz, jint nbChannels, jint audioFormat) {    int frameCount = 0;    if (AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,            sampleRateInHertz) != NO_ERROR) {        return -1;    }    return frameCount * nbChannels * (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1);}

上面这段本地代码告诉了我那个最小的buffer区域的大小的计算公式,并且在这个方法中引入了另一重要的变量frameCount,暂时不知道是什么意思,但这并不影响我分析这段代码,代码告诉我们buffer size = frameCount * 声道个数(单声道 or 双声道?1或2) * 音频数据格式所占的字节(2字节或1字节)

好吧那么让我再踏上分析frameCount由来的历程吧,根据字面意思是帧计数的意思,再跳到getMinFrameCount方法的实现部分代码去看看不过在分析status_t AudioTrack::getMinFrameCount(
int* frameCount,
int streamType,
uint32_t sampleRate)这个蛋痛的方法前还先来一段小插曲吧:Android系统中的声音流类型,即Audio Stream Type,定义在system/core/include/system/audio.h中

/* *Audio stream types */ typedef enum { AUDIO_STREAM_DEFAULT = -1, AUDIO_STREAM_VOICE_CALL = 0, /*电话声音*/ AUDIO_STREAM_SYSTEM = 1, /*系统声音*/ AUDIO_STREAM_RING = 2, /*铃 音*/ AUDIO_STREAM_MUSIC = 3, /*音乐声音*/ AUDIO_STREAM_ALARM = 4, /*警告声音*/ AUDIO_STREAM_NOTIFICATION = 5, /*通知声音*/ AUDIO_STREAM_BLUETOOTH_SCO = 6, /*bluetooth*/ AUDIO_STREAM_ENFORCED_AUDIBLE = 7, /* Sounds that cannot be muted by user and must be routed to speaker */ AUDIO_STREAM_DTMF = 8, AUDIO_STREAM_TTS = 9, AUDIO_STREAM_CNT, AUDIO_STREAM_MAX = AUDIO_STREAM_CNT - 1, } audio_stream_type_t; 
这个参数和Android中的AudioManager有关系的,举个例子吧,当我们在听music的时候突然来了一个电话,此刻音乐没了,你能听见的是来电的声音当我们去调节音量的时候

此刻的调节只会对接电话起作用,这个参数对AudioTrack来说,它的含义就是告诉系统,我现在想使用的是哪种类型的声音,这样系统可以更好的对他们进行分类管理。

好了我们回到getMinFrameCount源码片段

/*static  *static */status_t AudioTrack::getMinFrameCount(        int* frameCount,        int streamType,        uint32_t sampleRate){    int afSampleRate;    if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {        return NO_INIT;    }    int afFrameCount;    if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {        return NO_INIT;    }    uint32_t afLatency;    if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) {        return NO_INIT;    }    // Ensure that buffer depth covers at least audio hardware latency    uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);    if (minBufCount < 2) minBufCount = 2;    *frameCount = (sampleRate == 0) ? afFrameCount * minBufCount :              afFrameCount * minBufCount * sampleRate / afSampleRate;    return NO_ERROR;}

经过查看代码真的发现太恐怖了,层层调用不知在何方才能见光明,唉,此乃一条不归路啊,总的来说反正谷歌只管更新源码,却不管开发人员的死活,再说了累死人他们有不要填命的。

getMinFrameCount()方法最终返回了我们梦寐以求的frameCount 引用计数指针。可是它的实现过程却是很复杂,getMinFrameCount方法会根据streamType的类型通过AudioSystem提供的几个AP去获取afSampleRate,afFrameCount,afLatency 这三个指标,至于这三个指标,我的理解也有点模糊,望高手指点,然后又根据这三个指标去计算最小的buff计数,最后根据buf最小计数计算出帧计数(frameCount)。另外一个音频帧的理解,一个音频帧即frame=一个采样点所占的字节数 * 声道数。最后根据frameCount再回到过去计算我们创建AudioTrack所需要申请的最小缓冲区的bufferSize。这样用户分配最小的缓冲区就有依据了

至此我们还是完成了地一个目标,下一步就是第二个目标AudioTrack的创建过程

让我们又回到代码的原点,从心开始吧,继续回到MediaAudioTrackTest.java中的testSetStereoVolumeMax方法查看newAudioTrack()的实现过程

AudioTrack类的构造函数原型

/**     * Class constructor.     * @param streamType the type of the audio stream. See     *   {@link AudioManager#STREAM_VOICE_CALL}, {@link AudioManager#STREAM_SYSTEM},     *   {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC} and     *   {@link AudioManager#STREAM_ALARM}     * @param sampleRateInHz the sample rate expressed in Hertz. Examples of rates are (but     *   not limited to) 44100, 22050 and 11025.     * @param channelConfig describes the configuration of the audio channels.     *   See {@link AudioFormat#CHANNEL_OUT_MONO} and     *   {@link AudioFormat#CHANNEL_OUT_STEREO}     * @param audioFormat the format in which the audio data is represented.     *   See {@link AudioFormat#ENCODING_PCM_16BIT} and     *   {@link AudioFormat#ENCODING_PCM_8BIT}     * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is read     *   from for playback. If using the AudioTrack in streaming mode, you can write data into     *   this buffer in smaller chunks than this size. If using the AudioTrack in static mode,     *   this is the maximum size of the sound that will be played for this instance.     *   See {@link #getMinBufferSize(int, int, int)} to determine the minimum required buffer size     *   for the successful creation of an AudioTrack instance in streaming mode. Using values     *   smaller than getMinBufferSize() will result in an initialization failure.     * @param mode streaming or static buffer. See {@link #MODE_STATIC} and {@link #MODE_STREAM}     * @throws java.lang.IllegalArgumentException     */    public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,            int bufferSizeInBytes, int mode)    throws IllegalArgumentException {        this(streamType, sampleRateInHz, channelConfig, audioFormat,                bufferSizeInBytes, mode, 0);    }

上面的注释很给力,它的实现继承了AudioTrack的另一个构造方法,如下:

/** *return AudioTrack  */ public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,            int bufferSizeInBytes, int mode, int sessionId)    throws IllegalArgumentException {        mState = STATE_UNINITIALIZED;                /*获得主线程的Looper 这个好久没敲代码忘记了。。*/        if ((mInitializationLooper = Looper.myLooper()) == null) {            mInitializationLooper = Looper.getMainLooper();        }/* *对上面我们传入的那些参数做一个检测 *streamType = AudioManager.STREAM_MUSIC 音乐声 *sampleRateInHz = 22050采样率 22050 Hz *channelConfig = CHANNEL_OUT_STEREO    双声道 *audioFormat = AudioFormat.ENCODING_PCM_16BIT 音频数据格式 2字节的 *mode = MODE_STREAM  *bufferSizeInBytes 这个就是我们上面千辛万苦得来的*/        audioParamCheck(streamType, sampleRateInHz, channelConfig, audioFormat, mode);        audioBuffSizeCheck(bufferSizeInBytes);        if (sessionId < 0) {            throw (new IllegalArgumentException("Invalid audio session ID: "+sessionId));        }。。。。。。。。。。。。。。。。。。        /*调用本地方法 参数列表就是上面注释部分的那些*/        int initResult = native_setup(new WeakReference<AudioTrack>(this),                mStreamType, mSampleRate, mChannels, mAudioFormat,                mNativeBufferSizeInBytes, mDataLoadMode, session);        if (initResult != SUCCESS) {            loge("Error code "+initResult+" when initializing AudioTrack.");            return; // with mState == STATE_UNINITIALIZED        }。。。。。。。。。。。。。。           }

AudioTrackde 的构造方法最后调用了frameworks/base/core/jni/android_media_AudioTrack.cpp中的native_setup本地方法,其中native_setup又被映射成(void *)android_media_AudioTrack_native_setup

/** *gMethods */static JNINativeMethod gMethods[] = {    // name,              signature,     funcPtr    {"native_start",         "()V",      (void *)android_media_AudioTrack_start},    {"native_stop",          "()V",      (void *)android_media_AudioTrack_stop},    {"native_pause",         "()V",      (void *)android_media_AudioTrack_pause},    {"native_flush",         "()V",      (void *)android_media_AudioTrack_flush},    {"native_setup",         "(Ljava/lang/Object;IIIIII[I)I",                                         (void *)android_media_AudioTrack_native_setup},}
最后经过android_media_AudioTrack_native_setup本地方法做了一大堆的判断和处理返回AUDIOTRACK_SUCCESS,表示构建AudioTrack成功,真是悲剧.
/*  *android_media_AudioTrack_native_setup*/static intandroid_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,        jint streamType, jint sampleRateInHertz, jint javaChannelMask,        jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession){   。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。    // compute the frame count    int bytesPerSample = audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1;    int format = audioFormat == javaAudioTrackFields.PCM16 ?             AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT;    int frameCount = buffSizeInBytes / (nbChannels * bytesPerSample);        AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();        // initialize the callback information:    // this data will be passed with every AudioTrack callback    jclass clazz = env->GetObjectClass(thiz);    if (clazz == NULL) {        LOGE("Can't find %s when setting up callback.", kClassPathName);        delete lpJniStorage;        return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;    }    lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);    // we use a weak reference so the AudioTrack object can be garbage collected.    lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);        lpJniStorage->mStreamType = atStreamType;    if (jSession == NULL) {        LOGE("Error creating AudioTrack: invalid session ID pointer");        delete lpJniStorage;        return AUDIOTRACK_ERROR;    }    jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);    if (nSession == NULL) {        LOGE("Error creating AudioTrack: Error retrieving session id pointer");        delete lpJniStorage;        return AUDIOTRACK_ERROR;    }    int sessionId = nSession[0];    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);    nSession = NULL;    /*最终的目标音频系统的播放接口由AudioTrack提供每一个音频都会对应一个AudioTrack实例,这里请注意这个实例是本地方法new出来的而非上层AP*/    AudioTrack* lpTrack = new AudioTrack();    if (lpTrack == NULL) {        LOGE("Error creating uninitialized AudioTrack");        goto native_track_failure;    }        // initialize the native AudioTrack object    if (memoryMode == javaAudioTrackFields.MODE_STREAM) {        lpTrack->set(/*这个方法会引出AudioFlinger对象的出现*/            atStreamType,// stream type            sampleRateInHertz,            format,// word length, PCM            nativeChannelMask,            frameCount,            0,// flags            audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)            0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack            0,// shared mem            true,// thread can call Java            sessionId);// audio session ID                } else if (memoryMode == javaAudioTrackFields.MODE_STATIC) {        // AudioTrack is using shared memory                if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {            LOGE("Error creating AudioTrack in static mode: error creating mem heap base");            goto native_init_failure;        }                lpTrack->set(            atStreamType,// stream type            sampleRateInHertz,            format,// word length, PCM            nativeChannelMask,            frameCount,            0,// flags            audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));            0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack             lpJniStorage->mMemBase,// shared mem            true,// thread can call Java            sessionId);// audio session ID    }    /*在上面的set方法中会调用本地AudioTrack的creatTrack函数AudioFlinger会根据传入的frameCount参数申请一块共享内存*/    if (lpTrack->initCheck() != NO_ERROR) {        LOGE("Error initializing AudioTrack");        goto native_init_failure;    }    nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);    if (nSession == NULL) {        LOGE("Error creating AudioTrack: Error retrieving session id pointer");        goto native_init_failure;    }    // read the audio session ID back from AudioTrack in case we create a new session    nSession[0] = lpTrack->getSessionId();    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);    nSession = NULL;    // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field     // of the Java object (in mNativeTrackInJavaObj)    env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (int)lpTrack);        // save the JNI resources so we can free them later    //LOGV("storing lpJniStorage: %x\n", (int)lpJniStorage);    env->SetIntField(thiz, javaAudioTrackFields.jniData, (int)lpJniStorage);    /*最后将本地AudioTrack和上次AP的AudioTrack相互关联,并且通过AudioFlinger对这块共享内存进行操作,从而进行数据的交互*/    return AUDIOTRACK_SUCCESS;    。。。。。。。。。}

留点悬念吧,AudioTrack的历程还没结束,随着AudioFlinger的出现会引发出一系列的血案的

更多相关文章

  1. 浅谈Java中Collections.sort对List排序的两种方法
  2. Python list sort方法的具体使用
  3. python list.sort()根据多个关键字排序的方法实现
  4. Android应用程序启动过程源代码分析
  5. Android开发笔记——以Volley图片加载、缓存、请求及展示为例理
  6. Android中AsyncTask的简单用法
  7. AndroidHttpClient使用Cookie应用分析
  8. Android高手进阶教程(七)----Android(安卓)中Preferences的使用!
  9. android滑动菜单demo

随机推荐

  1. PHP新手入门1——表单
  2. 在Ubuntu上为Android增加硬件抽象层(HAL)模
  3. Android(安卓)UI系列-----LinearLayout的
  4. android 控制POS机图文打印(二)
  5. Android开发笔记(八十五)手机数据库Realm
  6. Android开发学习笔记(十)实现一个简易的开
  7. Android使用Intent启动其他非系统应用程
  8. Android中bluetooth的架构
  9. Android(安卓)activity与service中的子线
  10. Eclipse如何打包生成Apk安装包实例(图)