android audio 音量設置分析



 轉載自:http://blog.csdn.net/wan8180192/article/details/50705882

 audiod 中經常遇到的場景是音量調整與輸出設備的切換,下面兩篇文章 針對這兩個場景分別分析一下
1,音量調整場景
android 音量調整,可以使用兩種方式: 
軟件mixer的時候修改PCM data  
控制DAC硬件的增益

第一種情況,如果是多路mix的情況,就是MixerThread進行軟件mixer,然後在mixer計算的時候來縮放PCM data,
首先,JNI層調用了AudioFlinger::setStreamVolume。

  1. status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,  
  2.         audio_io_handle_t output)  
  3. {  
  4.     AutoMutex lock(mLock);  
  5.     PlaybackThread *thread = NULL;  
  6.     if (output) {  
  7.         thread = checkPlaybackThread_l(output);  //獲得對應的PlaybackThread  
  8.         if (thread == NULL) {  
  9.             return BAD_VALUE;  
  10.         }  
  11.     }  
  12.     if (thread == NULL) {  
  13.         for (size_t i = 0; i < mPlaybackThreads.size(); i++) {  
  14.             mPlaybackThreads.valueAt(i)->setStreamVolume(stream, value);  
  15.         }  
  16.     } else {  
  17.         thread->setStreamVolume(stream, value);  //繼續向下層設置  
  18.     }  
  19.     return NO_ERROR;  
  20. }  
status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,
        audio_io_handle_t output)
{
    AutoMutex lock(mLock);
    PlaybackThread *thread = NULL;
    if (output) {
        thread = checkPlaybackThread_l(output);  //獲得對應的PlaybackThread
        if (thread == NULL) {
            return BAD_VALUE;
        }
    }
    if (thread == NULL) {
        for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
            mPlaybackThreads.valueAt(i)->setStreamVolume(stream, value);
        }
    } else {
        thread->setStreamVolume(stream, value);  //繼續向下層設置
    }
    return NO_ERROR;
}
可以看到,最終是調用了PlaybackThread::setStreamVolume來繼續設置音量
  1. void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)  
  2. {  
  3.     Mutex::Autolock _l(mLock);  
  4.     mStreamTypes[stream].volume = value;//把音量數據存起來  
  5.     broadcast_l();  
  6. }  
void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
{
    Mutex::Autolock _l(mLock);
    mStreamTypes[stream].volume = value;//把音量數據存起來
    broadcast_l();
}
  1. AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l()  
  2. {  
  3.                 float typeVolume = mStreamTypes[track->streamType()].volume;  //取出暫存的音量數據  
  4.                 float v = masterVolume * typeVolume;  
  5.                 AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy;  
  6.                 uint32_t vlr = proxy->getVolumeLR();  
  7.                 vl = vlr & 0xFFFF;  
  8.                 vr = vlr >> 16;  
  9.                 vl = (uint32_t)(v * vl) << 12;  
  10.                 vr = (uint32_t)(v * vr) << 12;  
  11.             mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, (void *)vl);  //設置給audioMixer  
  12.             mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, (void *)vr);  
  13. }  
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l()
{
                float typeVolume = mStreamTypes[track->streamType()].volume;  //取出暫存的音量數據
                float v = masterVolume * typeVolume;
                AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy;
                uint32_t vlr = proxy->getVolumeLR();
                vl = vlr & 0xFFFF;
                vr = vlr >> 16;
                vl = (uint32_t)(v * vl) << 12;
                vr = (uint32_t)(v * vr) << 12;
            mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, (void *)vl);  //設置給audioMixer
            mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, (void *)vr);
}
可以看到上述函數把參數設置到了audioMixer裏面,在audioMixer章節裏面,
我們介紹過在audioMixer的實際操作函數是track__16BitsStereo這種函數,
對track__16BitsStereo的分析中,我們可以看到根據音量對PCM data進行實際的縮放
不再繼續贅述了。

對於第二種方式,控制DAC硬件的增益,主要用在了DirectOutputThread,中,因爲DirectOutputThread只有一路音頻,直接寫入HAL層,直接寫入硬件的,
所以需要直接調用硬件DAC芯片的控制接口來調整音量。
其主要流程如下:
和mixerThread的流程一樣,上層在調用了AudioFlinger::setStreamVolume之後,會調用prepareTracks_l函數
  1. AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prepareTracks_l(  
  2.     Vector< sp<Track> > *tracksToRemove  
  3. )  
  4. {  
  5.             // compute volume for this track  
  6.             processVolume_l(track, last);  
  7. }  
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prepareTracks_l(
    Vector< sp<Track> > *tracksToRemove
)
{
            // compute volume for this track
            processVolume_l(track, last);
}
AT章節中,我們提到過prepareTracks_l函數,其中會調用processVolume_l來處理音量
  1. void AudioFlinger::DirectOutputThread::processVolume_l(Track *track, bool lastTrack)  
  2. {  
  3.     audio_track_cblk_t* cblk = track->cblk();  
  4.     float left, right;  
  5.         float typeVolume = mStreamTypes[track->streamType()].volume;  //和mixerThread一樣,也是從mStreamTypes裏面取出音量數據  
  6.         float v = mMasterVolume * typeVolume;  
  7.         AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy;  
  8.         uint32_t vlr = proxy->getVolumeLR();  
  9.         float v_clamped = v * (vlr & 0xFFFF);  
  10.         if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;  
  11.         left = v_clamped/MAX_GAIN;  
  12.         v_clamped = v * (vlr >> 16);  
  13.         if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;  
  14.         right = v_clamped/MAX_GAIN;  
  15.     if (lastTrack) {  
  16.         if (left != mLeftVolFloat || right != mRightVolFloat) {  
  17.             mLeftVolFloat = left;  
  18.             mRightVolFloat = right;  
  19.             uint32_t vl = (uint32_t)(left * (1 << 24));  
  20.             uint32_t vr = (uint32_t)(right * (1 << 24));  
  21.             if (mOutput->stream->set_volume) {  
  22.                 mOutput->stream->set_volume(mOutput->stream, left, right);  //向下層設置音量  
  23.             }  
  24.         }  
  25.     }  
  26. }  
void AudioFlinger::DirectOutputThread::processVolume_l(Track *track, bool lastTrack)
{
    audio_track_cblk_t* cblk = track->cblk();
    float left, right;
        float typeVolume = mStreamTypes[track->streamType()].volume;  //和mixerThread一樣,也是從mStreamTypes裏面取出音量數據
        float v = mMasterVolume * typeVolume;
        AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy;
        uint32_t vlr = proxy->getVolumeLR();
        float v_clamped = v * (vlr & 0xFFFF);
        if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
        left = v_clamped/MAX_GAIN;
        v_clamped = v * (vlr >> 16);
        if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
        right = v_clamped/MAX_GAIN;
    if (lastTrack) {
        if (left != mLeftVolFloat || right != mRightVolFloat) {
            mLeftVolFloat = left;
            mRightVolFloat = right;
            uint32_t vl = (uint32_t)(left * (1 << 24));
            uint32_t vr = (uint32_t)(right * (1 << 24));
            if (mOutput->stream->set_volume) {
                mOutput->stream->set_volume(mOutput->stream, left, right);  //向下層設置音量
            }
        }
    }
}
mOutput->stream->set_volume實際上調用的是 libhardware_legacy中的函數
  1. static int out_set_volume(struct audio_stream_out *stream, float left,  //調用libhardware_legacy中的函數  
  2.                           float right)  
  3. {  
  4.     struct legacy_stream_out *out =  
  5.         reinterpret_cast<struct legacy_stream_out *>(stream);  
  6.     return out->legacy_out->setVolume(left, right);  
  7. }  
static int out_set_volume(struct audio_stream_out *stream, float left,  //調用libhardware_legacy中的函數
                          float right)
{
    struct legacy_stream_out *out =
        reinterpret_cast<struct legacy_stream_out *>(stream);
    return out->legacy_out->setVolume(left, right);
}
然後就進入了HAL層代碼,HAL層代碼中最終調用了 ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
寫入驅動
由於商業機密,HAL層代碼不能貼出來。
到了內核中,則是按照以下調用序列,最終通過IIC總線,將音量控制命令寫入了DAC芯片中
  1. ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);  
  2. snd_ctl_elem_write_user  
  3. snd_ctl_elem_write_user  
  4. snd_ctl_elem_write  
  5. wm8523_controls  
  6. snd_soc_put_volsw  
  7. snd_soc_update_bits_locked  
ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
snd_ctl_elem_write_user
snd_ctl_elem_write_user
snd_ctl_elem_write
wm8523_controls
snd_soc_put_volsw
snd_soc_update_bits_locked

IIC總線寫入命令
關於驅動的流程我們在這裏不展開講,後續會單獨講解

其他系列可參考

http://blog.csdn.net/WAN8180192/article/category/6103460


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章