草庐IT

Android音频——音量调节

xiaopangcame 2023-04-25 原文

一、音量相关概念

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" />

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音频——音量调节的更多相关文章

  1. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  2. micropython复现经典单片机项目(二)可视化音频 频谱解析(基本搞定) - 2

    本人是音乐爱好者,从小就特别喜欢那个随着音乐跳动的方框效果,就是这个:arduino上一大把对,我忍你很久了,我就想用mpy做,全网没有,行我自己研究。果然兴趣是最好的老师,我之前有篇博客专门讲音频,有兴趣的可以回顾一下。提到可视化频谱,必然绕不开fft,大学学过这玩意,当时一心玩,老师讲的一个字都么听进去,网上教程简略扫了一下,大该就是把时域转频域的工具,我大mpy居然没有fft函数,奶奶的,先放着。音频信息如何收集?第一种傻瓜式的ADC,模拟转数字,原始粗暴,第二种,I2S库,我之前博客有讲过,数据是PCM编码。然后又去学PCM编码,一学豁然开朗,舒服,以代码为例:audio_in=I2S

  3. Android Studio开发之使用内容组件Content获取通讯信息讲解及实战(附源码 包括添加手机联系人和发短信) - 2

    运行有问题或需要源码请点赞关注收藏后评论区留言一、利用ContentResolver读写联系人在实际开发中,普通App很少会开放数据接口给其他应用访问。内容组件能够派上用场的情况往往是App想要访问系统应用的通讯数据,比如查看联系人,短信,通话记录等等,以及对这些通讯数据及逆行增删改查。首先要给AndroidMaifest.xml中添加响应的权限配置 下面是往手机通讯录添加联系人信息的例子效果如下分成三个步骤先查出联系人的基本信息,然后查询联系人号码,再查询联系人邮箱代码 ContactAddActivity类packagecom.example.chapter07;importandroid

  4. Android 10.0 设置默认launcher后安装另外launcher后默认Launcher失效的功能修复 - 2

    1.前言 在10.0的系统rom定制化开发中,在系统中有多个launcher的时候,会在开机进入launcher的时候弹窗launcher列表,让用户选择进入哪个launcher,这样显得特别的不方便所以产品开发中,要求用RoleManager的相关api来设置默认Launcher,但是在设置完默认Launcher以后,在安装一款Launcher的时候,默认Launcher就会失效,在系统设置的默认应用中Launcher选项就为空,点击home键的时候会弹出默认Launcher列表,让选择进入哪个默认Launcher.所以需要从安装Launcher的流程来分析相关的设置。来解决问题设置默认La

  5. 解决台式机麦克风不可用问题,只有音频输出,无音频输入 - 2

    解决台式机麦克风不可用问题戴尔灵越3880最近因为需要开线上会议,发现戴尔台式机音频只有输出没有输入,也就是只能听见声音,无法输入声音。先后尝试了各种驱动安装更新之类的调试,无果。之后通过戴尔支持解决~这里多说一句,专业的就是专业,问题描述过去,直接给了解决方案,可能是他们遇到的相似问题比较多了,但也告诉我们,有些时候是可以通过这些官方服务解决问题的,比起自己折腾效率要高很多。那就记录一下吧~问题描述:电脑只能输出声音,不能输入声音。1、前提需要准备一只带麦克风的耳机,将耳机插入面板。2、先确定是否可以听到声音,可以通过播放歌曲或者视频。3、然后确认麦克风是否可用,可以通过调用win自带麦克风

  6. AiBote 2022 新研发的自动化框架,支持 Android 和 Windows 系统。速度非常快 - 2

    Ai-Bot基于流行的Node.js和JavaScript语言的一款新自动化框架,支持Windows和Android自动化。1、Windowsxpath元素定位算法支持支持Windows应用、.NET、WPF、Qt、Java和Electron客户端程序和ie、edgechrome浏览器2、Android支持原生APP和H5界面,元素定位速度是appium十倍,无线远程自动化操作多台安卓设备3、基于opencv图色算法,支持找图和多点找色,1080*2340全分辨率找图50MS以内4、内置免费OCR人工智能技术,无限制获取图片文字和找字功能。5、框架协议开源,除官方node.jsSDK外,用户可

  7. Android Gradle 7.1+新版本依赖变化 - 2

    前一段时间由于工作需要把可爱的小雪狐舍弃了,找到了小蜜蜂。但是新版本的小蜜蜂出现了很多和旧版本不一样的位置。1.功能位置迁移,原来在工程build.gradle的buildscript和allprojects移动至setting.gradle并改名为pluginManagement和dependencyResolutionManagement。里面的东西依旧可以按照原来的copy过来。pluginManagement{repositories{gradlePluginPortal()google()mavenCentral()}}dependencyResolutionManagement{r

  8. ruby - Ruboto 的最佳教程(适用于 Android 的 ruby​​)? - 2

    关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于StackOverflow来说是偏离主题的,因为它们往往会吸引自以为是的答案和垃圾邮件。相反,describetheproblem以及迄今为止为解决该问题所做的工作。关闭9年前。Improvethisquestion我几乎用完了Ruby,但现在想试试Ruboto,android上的ruby​​。谷歌未能给我足够的(几乎没有结果)。所以任何人都可以分享一些关于Ruboto的教程。

  9. ruby - 我将如何以编程方式与 VST(i) 插件交互以合成音频? - 2

    以VSTiTriforce为例,由Tweakbench提供。当加载到市场上的任何VST主机时,它允许主机向VSTi发送(大概是MIDI)信号。然后VSTi将处理该信号并输出​​由VSTi内的软件乐器创建的合成音频。例如,将A4(我相信是MIDI音符)发送到VSTi会导致它合成高于中央C的A。它将音频数据发送回VST主机,然后它可以在我的扬声器上播放或将其保存为.wav或其他一些音频文件格式。假设我有Triforce,我正在尝试用我选择的语言编写一个程序,它可以通过发送要合成的A4纸条与VSTi交互,并自动将其保存到系统上的文件?最终,我希望能够解析整个单轨MIDI文件(使用已经可用于此

  10. Android Studio 解决Could not resolve com.android.tools.build:gradle:7.4.2问题 - 2

    Aproblemoccurredconfiguringrootproject'MyApplication2'.>Couldnotresolveallfilesforconfiguration':classpath'.  >Couldnotresolvecom.android.tools.build:gradle:7.4.2.   Requiredby:     project:>com.android.application:com.android.application.gradle.plugin:7.4.2     project:>com.android.library:com.andr

随机推荐