深入剖析Android音频之AudioTrack

本文详细探讨了Android中AudioTrack的工作原理,从构造过程开始,包括AudioTrack的构造、AudioTrackJniStorage的创建、初始化,以及AudioTrack的启动、数据写入和停止过程。重点讲述了AudioTrack如何与AudioFlinger交互,涉及数据传输模式、音频参数设置和线程管理。此外,还讨论了AudioTrack在处理PCM流时的角色,以及与MediaPlayer的区别。
摘要由CSDN通过智能技术生成

原文:https://www.cnblogs.com/mfmdaoyou/p/7348969.html

深入剖析Android音频之AudioTrack

播放声音能够用MediaPlayer和AudioTrack,两者都提供了java API供应用开发人员使用。尽管都能够播放声音。但两者还是有非常大的差别的。当中最大的差别是MediaPlayer能够播放多种格式的声音文件。比如MP3。AAC,WAV,OGG。MIDI等。

MediaPlayer会在framework层创建相应的音频解码器。而AudioTrack仅仅能播放已经解码的PCM流,假设是文件的话仅仅支持wav格式的音频文件,由于wav格式的音频文件大部分都是PCM流。AudioTrack不创建解码器。所以仅仅能播放不须要解码的wav文件。

当然两者之间还是有紧密的联系,MediaPlayer在framework层还是会创建AudioTrack,把解码后的PCM数流传递给AudioTrack。AudioTrack再传递给AudioFlinger进行混音,然后才传递给硬件播放,所以是MediaPlayer包括了AudioTrack。使用AudioTrack播放音乐演示样例:

AudioTrack audio = new AudioTrack(
     AudioManager.STREAM_MUSIC, // 指定流的类型
     32000, // 设置音频数据的採样率 32k,假设是44.1k就是44100
     AudioFormat.CHANNEL_OUT_STEREO, // 设置输出声道为双声道立体声,而CHANNEL_OUT_MONO类型是单声道
     AudioFormat.ENCODING_PCM_16BIT, // 设置音频数据块是8位还是16位。这里设置为16位。

 

好像如今绝大多数的音频都是16位的了 AudioTrack.MODE_STREAM // 设置模式类型,在这里设置为流类型,第二种MODE_STATIC貌似没有什么效果 ); audio.play(); // 启动音频设备。以下就能够真正開始音频数据的播放了 // 打开mp3文件,读取数据,解码等操作省略 ... byte[] buffer = new buffer[4096]; int count; while(true) { // 最关键的是将解码后的数据,从缓冲区写入到AudioTrack对象中 audio.write(buffer, 0, 4096); if(文件结束) break; } //关闭并释放资源 audio.stop(); audio.release();

AudioTrack构造过程

 

每个音频流相应着一个AudioTrack类的一个实例,每个AudioTrack会在创建时注冊到 AudioFlinger中。由AudioFlinger把全部的AudioTrack进行混合(Mixer)。然后输送到AudioHardware中进行播放。眼下Android同一时候最多能够创建32个音频流,也就是说。Mixer最多会同一时候处理32个AudioTrack的数据流。

 

frameworks\base\media\java\android\media\AudioTrack.java

/**
 * streamType:音频流类型
 * sampleRateInHz:採样率
 * channelConfig:音频声道
 * audioFormat:音频格式
 * bufferSizeInBytes缓冲区大小:
 * mode:音频数据载入模式
 * sessionId:会话id
 */
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
        int bufferSizeInBytes, int mode, int sessionId)
throws IllegalArgumentException {
    // mState already == STATE_UNINITIALIZED

    // remember which looper is associated with the AudioTrack instantiation
    Looper looper;
    if ((looper = Looper.myLooper()) == null) {
        looper = Looper.getMainLooper();
    }
    mInitializationLooper = looper;
    /**
     * 參数检查
     * 1.检查streamType是否为:STREAM_ALARM、STREAM_MUSIC、STREAM_RING、STREAM_SYSTEM、STREAM_VOICE_CALL、
     *  STREAM_NOTIFICATION、STREAM_BLUETOOTH_SCO、STREAM_BLUETOOTH_SCO,并赋值给mStreamType
     * 2.检查sampleRateInHz是否在4000到48000之间。并赋值给mSampleRate
     * 3.设置mChannels: 
     *      CHANNEL_OUT_DEFAULT、CHANNEL_OUT_MONO、CHANNEL_CONFIGURATION_MONO ---> CHANNEL_OUT_MONO
     *      CHANNEL_OUT_STEREO、CHANNEL_CONFIGURATION_STEREO                  ---> CHANNEL_OUT_STEREO
     * 4.设置mAudioFormat: 
     *      ENCODING_PCM_16BIT、ENCODING_DEFAULT ---> ENCODING_PCM_16BIT
     *      ENCODING_PCM_8BIT ---> ENCODING_PCM_8BIT
     * 5.设置mDataLoadMode:
     *      MODE_STREAM
     *      MODE_STATIC
     */
    audioParamCheck(streamType, sampleRateInHz, channelConfig, audioFormat, mode);
    /**
     * buffer大小检查,计算每帧字节大小,假设是ENCODING_PCM_16BIT,则为mChannelCount * 2
     * mNativeBufferSizeInFrames为帧数
     */
    audioBuffSizeCheck(bufferSizeInBytes);
    if (sessionId < 0) {
        throw new IllegalArgumentException("Invalid audio session ID: "+sessionId);
    }
    //进入native层初始化
    int[] session = new int[1];
    session[0] = sessionId;
    // native initialization
    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
    }
    mSessionId = session[0];
    if (mDataLoadMode == MODE_STATIC) {
        mState = STATE_NO_STATIC_DATA;
    } else {
        mState = STATE_INITIALIZED;
    }
}

with audio session. Use this constructor when the AudioTrack must be attached to a particular audio session. The primary use of the audio session ID is to associate audio effects to a particular instance of AudioTrack: if an audio session ID is provided when creating an AudioEffect, this effect will be applied only to audio tracks and media players in the same session and not to the output mix. When an AudioTrack is created without specifying a session, it will create its own session which can be retreived by calling the getAudioSessionId() method. If a non-zero session ID is provided, this AudioTrack will share effects attached to this session with all other media players or audio tracks in the same session, otherwise a new session will be created for this track if none is supplied.

streamType

the type of the audio stream. See STREAM_VOICE_CALL,STREAM_SYSTEM,STREAM_RING,STREAM_MUSIC,STREAM_ALARM, andSTREAM_NOTIFICATION.

sampleRateInHz

the sample rate expressed in Hertz.

channelConfig

describes the configuration of the audio channels. SeeCHANNEL_OUT_MONO andCHANNEL_OUT_STEREO

audioFormat

the format in which the audio data is represented. SeeENCODING_PCM_16BIT andENCODING_PCM_8BIT

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. SeegetMinBufferSize(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.

mode

streaming or static buffer. See MODE_STATIC andMODE_STREAM

sessionId

Id of audio session the AudioTrack must be attached to

AudioTrack有两种数据载入模式:

  1. MODE_STREAM

在这样的模式下,应用程序持续地write音频数据流到AudioTrack中,而且write动作将堵塞直到数据流从Java层传输到native层,同一时候增加到播放队列中。这样的模式适用于播放大音频数据,但该模式也造成了一定的延时;

  1. MODE_STATIC

在播放之前,先把全部数据一次性write到AudioTrack的内部缓冲区中。适用于播放内存占用小、延时要求较高的音频数据。

 

frameworks\base\core\jni\android_media_AudioTrack.cpp

static int android_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)
{
    ALOGV("sampleRate=%d, audioFormat(from Java)=%d, channel mask=%x, buffSize=%d",
        sampleRateInHertz, audioFormat, javaChannelMask, buffSizeInBytes);
    int afSampleRate;//採样率
    int afFrameCount;//帧数
	//通过AudioSystem从AudioPolicyService中读取相应音频流类型的帧数
    if (AudioSystem::getOutputFrameCount(&afFrameCount, (audio_stream_type_t) streamType) != NO_ERROR) {
        ALOGE("Error creating AudioTrack: Could not get AudioSystem frame count.");
        return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM;
    }
    //通过AudioSystem从AudioPolicyService中读取相应音频流类型的採样率
    if (AudioSystem::getOutputSamplingRate(&afSampleRate, (audio_stream_type_t) streamType) != NO_ERROR) {
        ALOGE("Error creating AudioTrack: Could not get AudioSystem sampling rate.");
        return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM;
    }
    // Java channel masks don't map directly to the native definition, but it's a simple shift
    // to skip the two deprecated channel configurations "default" and "mono".
    uint32_t nativeChannelMask = ((uint32_t)javaChannelMask) >> 2;
	//推断是否为输出通道
    if (!audio_is_output_channel(nativeChannelMask)) {
        ALOGE("Error creating AudioTrack: invalid channel mask.");
        return AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
    }
    //得到通道个数,popcount函数用于统计一个整数中有多少位为1
    int nbChannels = popcount(nativeChannelMask);
    // check the stream type
    audio_stream_type_t atStreamType;
    switch (streamType) {
    case AUDIO_STREAM_VOICE_CALL:
    case AUDIO_STREAM_SYSTEM:
    case AUDIO_STREAM_RING:
    case AUDIO_STREAM_MUSIC:
    case AUDIO_STREAM_ALARM:
    case AUDIO_STREAM_NOTIFICATION:
    case AUDIO_STREAM_BLUETOOTH_SCO:
    case AUDIO_STREAM_DTMF:
        atStreamType = (audio_stream_type_t) streamType;
        break;
    default:
        ALOGE("Error creating AudioTrack: unknown stream type.");
        return AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE;
    }
    // This function was called from Java, so we compare the format against the Java constants
    if ((audioFormat != javaAudioTrackFields.PCM16) && (audioFormat != javaAudioTrackFields.PCM8)) {
        ALOGE("Error creating AudioTrack: unsupported audio format.");
        return AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;
    }
    // for the moment 8bitPCM in MODE_STATIC is not supported natively in the AudioTrack C++ class so we declare everything as 16bitPCM, the 8->16bit conversion for MODE_STATIC will be handled in android_media_AudioTrack_native_write_byte()
    if ((audioFormat == javaAudioTrackFields.PCM8)
        && (memoryMode == javaAudioTrackFields.MODE_STATIC)) {
        ALOGV("android_media_AudioTrack_native_setup(): requesting MODE_STATIC for 8bit \
            buff size of %dbytes, switching to 16bit, buff size of %dbytes",
            buffSizeInBytes, 2*buffSizeInBytes);
        audioFormat = javaAudioTrackFields.PCM16;
        // we will need twice the memory to store the data
        buffSizeInBytes *= 2;
    }
    //依据不同的採样方式得到一个採样点的字节数
    int bytesPerSample = audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1;
    audio_format_t format = audioFormat == javaAudioTrackFields.PCM16 ?
            AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT;
    //依据buffer大小反向计算帧数  。 一帧大小=一个採样点字节数 * 声道数
    int frameCount = buffSizeInBytes / (nbChannels * bytesPerSample);
    //推断參数的合法性
    jclass clazz = env->GetObjectClass(thiz);
    if (clazz == NULL) {
        ALOGE("Can't find %s when setting up callback.", kClassPathName);
        return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
    }
    if (jSession == NULL) {
        ALOGE("Error creating AudioTrack: invalid session ID pointer");
        return AUDIOTRACK_ERROR;
    }
    jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
    if (nSession == NULL) {
        ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
        return AUDIOTRACK_ERROR;
    }
    int sessionId = nSession[0];
    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
    nSession = NULL;
    // create the native AudioTrack object
    sp<AudioTrack> lpTrack = new AudioTrack();
    if (lpTrack == NULL) {
        ALOGE("Error creating uninitialized AudioTrack");
        return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
    }
    // 创建存储音频数据的容器
    AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();
    lpJniStorage->mStreamType = atStreamType;
    //将Java层的AudioTrack引用保存到AudioTrackJniStorage中
    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->mCallbackData.busy = false;
    //初始化不同模式下的native AudioTrack对象 
    if (memoryMode == javaAudioTrackFields.MODE_STREAM) { //stream模式
        lpTrack->set( 
            atStreamType,// stream type
            sampleRateInHertz,
            format,// word length, PCM
            nativeChannelMask,
            frameCount,
            AUDIO_OUTPUT_FLAG_NONE,
            audioCallback, 
            &(lpJniStorage->mCallbackData),//callback, callback data (user)
            0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
            0,//stream模式下的共享内存在AudioFlinger中创建
            true,// thread can call Java
            sessionId);// audio session ID
    } else if (memoryMode == javaAudioTrackFields.MODE_STATIC) {//static模式
        // 为AudioTrack分配共享内存区域 
        if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值