Android音頻(9)——音量調節


一、音量相關概念

1. 相關術語解釋

track volume : 單個App設置音量時設置的是這個,它只影響本App的音量。
stream volume :設置某一stream的音量,Android系統中支持10種stream。
stream volume alias:設置的是同一組stream的音量,比如使用某個音量調節滑動條設置的音量。比如設置媒體音,所有App的媒體音都受到影響(但是電話音,
鬧鍾音不受影響)。
master volume :設置它等於設置所有的stream volume和track volume。它可以寫到聲卡里面去,控制所有聲音的音量。也可以不寫到聲卡里面去,而是作為一個乘數因子來影響所有的音量。

2. 華為Honor8音量設置

設置-->聲音-->音量,設置界面列出了鈴聲、媒體、鬧鍾、通話,四個設置滾動條,稱為四個stream type,四組。
Android系統中有10種stream,在system/core/include/system/audio.h中定義。但把這10種stream分成組,屬於同一組的stream具有相同的別名(alias)。
一個音量調節滑動條具有一個alias,具有相同alias的stream都會受到這個滑動條的影響。

3. 聲音播放的兩種路徑

(1)MixerThread
對於MixerThread(多個App共用一個聲卡進行混音的的),APP對音量的設置不會影響到聲卡的硬件音量,而只會影響APP的音頻數據的幅值(變小或放大),
這些音頻數據最終被混合后傳給聲卡。多個APP本身的音量設置互不影響。

(2)DirectOutputThread
對於DirectOutputThread(對於HDMI的,單個音頻應用程序獨占使用一個聲卡的),同一時間里只有一個APP、只有一個AudioTrack使用它,
所以該AudioTrack的音量可以被DirectOutputThread直接用來設置硬件音量,這種聲卡使用的不多。

若audio_policy.conf中的output的參數信息(會被解析成一個output profile)中有"flags AUDIO_OUTPUT_FLAG_DIRECT"就表示這個聲卡可以
被某個App獨占。這個App就會以DirectOutputThread的形式來使用這個聲卡。


4. APP設置音量時互不影響, 這是AudioTrack volume

5. stream volume
可以引申出來: 各種stream的音量也可以單獨設置、互不影響。比如"音樂音量"不應該影響到"來電振鈴"、"鬧鍾"、"通話"的音量。

6. 有的手機音量控制界面有5種滑動條,用於設置某種類型的聲音音量,但是Android系統創建AudioTrack時可以指定10種stream type,
必須分組,在Android源碼中稱之為"別名", 即alias。
比如在電話中, 以下5種stream的alias都是STREAM_RING,那么對應的滑動條即可控制這5種stream的音量。
STREAM_SYSTEM
STREAM_RING
STREAM_NOTIFICATION
STREAM_SYSTEM_ENFORCED
STREAM_DTMF

6. 無論是AudioTrack volume、stream volume, 都是單獨設置. master volume 可以設置所有的AudioTrack volume和stream volume,也可
直接用來控制聲卡的寄存器。

7. 混音:
app1: data1_mix = data1_in * master_volume * stream1_volume * AudioTrack1_volume

app2: data2_mix = data2_in * master_volume * stream2_volume * AudioTrack2_volume

混合在一起: data_mix = data1_mix + data2_mix 然后把混合后的數據寫給硬件。

 

二、AudioFlinger層調節音量流程

1. AudioFlinger層調節音量流程
a. AudioFlinger對master volume, stream volume的初始化與設置
b. PlaybackThread對master volume, stream volume的初始化與設置
c. AudioTrack volume的設置
d. 這3種音量的使用


2. AudioFlinger類中有關成員:
stream_type_t mStreamTypes[AUDIO_STREAM_CNT];
//存儲master volume
float mMasterVolume;
//存儲是否靜音
bool mMasterMute;

2. playbackThread類中:
stream_type_t mStreamTypes[AUDIO_STREAM_CNT + 1]; //為DuplicatingThread的OutputTrack多出一項
bool mMasterMute;
float mMasterVolume; //來源於AudioFlinger中的同名的變量

3. AudioTrack類中(App端)
float mVolume[2]; //兩項,分別表示App設置的左右聲道的音量

4. stream volume和audioTreack中的volume只是軟件上的處理,masterVolue中保存的值若HAL提供了相應的寫函數就會寫給硬件。

5. DuplicatingThread可以用於在兩個聲卡上播放出同樣的聲音。

6. 加載HAL時設置為初始化值

AudioFlinger::loadHwModule(const char *name) //AudioFlinger.cpp
    loadHwModule_l(name);
        //調用HAL的open函數,得到一個audio_hw_device_t
        audio_hw_device_t *dev;
        load_audio_interface(name, &dev);
            //if_name來自audio_policy.conf,是"primary",AUDIO_HARDWARE_MODULE_ID是"audio"
            //最后組合成的名字就是: audio.primary.tiny4412.so
            hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod);
            audio_hw_device_open(mod, dev);
        //若HAL提供了get_master_volume就獲取硬件的值賦給mMasterVolume
        mMasterVolume = dev->get_master_volume(dev, &mv)
        //若HAL提供了get_master_mute就獲取硬件的值賦給mMasterMute
        mMasterMute = dev->get_master_mute(dev, &mm)
        //若存在對應的函數則調用設置
        dev->set_master_volume(dev, mMasterVolume)
        dev->set_master_mute(dev, mMasterMute)

audio_hw_device_t里面有masterVolume的存取函數:
typedef struct audio_hw_device audio_hw_device_t;
int (*set_master_mute)(struct audio_hw_device *dev, bool mute);
int (*get_master_mute)(struct audio_hw_device *dev, bool *mute);


AudioFlinger中還提供了函數設置MasterVolume和MasterMute
AudioFlinger::setMasterVolume(float value)
AudioFlinger::setMasterMute(bool muted)

AudioFlinger中的setStreamVolume
AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value, audio_io_handle_t output)
    mStreamTypes[stream].volume = value;
    AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value) //Threads.cpp
        mStreamTypes[stream].volume = value;
        broadcast_l();

PlaybackThread中的初始值都是來自AudioFlinger
AudioFlinger::PlaybackThread::PlaybackThread()
    mMasterVolume = audioFlinger->masterVolume_l();
    mMasterMute = audioFlinger->masterMute_l();
    //對於數組的每一項都執行
    mStreamTypes[stream].volume = mAudioFlinger->streamVolume_l(stream);
    mStreamTypes[stream].mute = mAudioFlinger->streamMute_l(stream);

AudioTrack中的
AudioTrack::setVolume(float left, float right) //AudioTrack.cpp
    mVolume[AUDIO_INTERLEAVE_LEFT] = left;
    mVolume[AUDIO_INTERLEAVE_RIGHT] = right;
    //
    mProxy->setVolumeLR(gain_minifloat_pack(gain_from_float(left), gain_from_float(right)));
        //mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
        //僅僅是把這個數據記錄在mVolumeLR域中而已。Cblk就是共享內存的頭部。
        mCblk->mVolumeLR = volumeLR; //AudioTrackShared.h

7. App中的AudioTrack與SurfaceFlinger中的mTracks中的對應項通過共享內存進行通信,這個mProxy就是共享內存的代理類。

8. App去設置音量只需要執行AudioTrack::setVolume就可以了。它會把設置的值放在自己的私有成員里面,也會放到共享內存的頭部。

9. 低16bit是左聲道數據,高16bit是右聲道數據

10. AudioMixer中的音量如何保存:音量有整數表示方式也有float表示方式,float表示方式是未來的發展趨勢

 

三、音量鍵和Setting界面調節音量流程

1. 對於seekBar控件,當滑動滑動條的時候,onProgressRefresh(AbsSeekBar.java)就會被調用,通過seekBar設置音量的兩種方法:
① 重寫onProgressRefresh
② 添加Listener

2. seekBar是通過packages目錄下的 notification_settings.xml 文件畫出來的,packages/apps/Settings/res/xml/notification_settings.xml

<!-- Media volume -->
<com.android.settings.notification.VolumeSeekBarPreference
        android:key="media_volume"
        android:icon="@drawable/ic_audio_vol_24dp"
        android:title="@string/media_volume_option_title" />

<!-- Alarm volume -->
<com.android.settings.notification.VolumeSeekBarPreference
        android:key="alarm_volume"
        android:icon="@drawable/ic_audio_alarm_24dp"
        android:title="@string/alarm_volume_option_title" />

<!-- Ring volume -->
<com.android.settings.notification.VolumeSeekBarPreference
        android:key="ring_volume"
        android:icon="@drawable/ring_notif"
        android:title="@string/ring_volume_option_title" />

<!-- Notification volume -->
<com.android.settings.notification.VolumeSeekBarPreference
        android:key="notification_volume"
        android:icon="@drawable/ring_notif"
        android:title="@string/notification_volume_option_title" />
View Code

3. 音量鍵和Setting界面調節音量流程

a. 音量鍵處理流程
音量鍵: 
  如果APP沒有重寫它的處理函數,音量鍵的處理將交給 PhoneFallbackEventHandler 來處理,它會調用AudioService.adjustSuggestedStreamVolume
  調整"推薦的流"的音量

a.1 如何獲得"推薦的流": stream = getActiveStreamType(...)

a.2 音量設置的"alias"如何起作用: 
     set volume for stream; // 設置"推薦的流"的音量
     
     if (mStreamVolumeAlias[other stream] == stream)
         set volume for other stream;  // 設置同屬一個alias的其他流的音量
         
     對於不同的設備(電話、TV、平板), mStreamVolumeAlias指向不同的數組

a.3 怎么設置流的音量:
    設置audioflinger中的數組:   mStreamTypes[stream].volume = value;
    設置PlaybackThread中的數組: mStreamTypes[stream].volume = value;
    

b. 音量滑動條處理流程

b.1 通過下面文件定義音量滑動條:
packages/apps/Settings/res/xml/notification_settings.xml
   該文件定義了多個VolumeSeekBarPreference,每個VolumeSeekBarPreference要跟一個SeekBar綁定

b.2 在VolumeSeekBarPreference的綁定函數onBindView中,設置了對應的SeekBar的SeekBarChangeListener (一個SeekBarVolumizer對象)

b.3 當SeekBar被滑動時, 它的onProgressRefresh被調用,該函數會調用 mOnSeekBarChangeListener.onProgressChanged

b.4 mOnSeekBarChangeListener.onProgressChanged去設置音量

b.5 每一個SeekBar對應一個stream,滑動SeekBar時會設置該stream的音量,也會去設置同屬一個alias(同一分組)的其他stream的音量
    
c. 兩者最終都會調用AudioService.java的代碼發出MSG:
            sendMsg(mAudioHandler,
                    MSG_SET_DEVICE_VOLUME,
                    SENDMSG_QUEUE,
                    device,
                    0,
                    streamState,
                    0);

 

參考:
Android官方setting文檔:https://developer.android.google.cn/guide/topics/ui/settings

android5.0設置模塊音量調節流程:https://blog.csdn.net/fireness/article/details/46738643

Android音量調節的實現(RingtoneManager和RingerVolumePreference):https://blog.csdn.net/liranke/article/details/6683000

android設置中拖動音量條調節音量流程(android5.1):https://blog.csdn.net/qq_28534581/article/details/77337599

Android 音量控制流程分析:https://blog.csdn.net/kehyuanyu/article/details/49153223

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM