From

audiod 中经常遇到的场景是音量调整与输出设备的切换,下面两篇文章 针对这两个场景分别分析一下

1.第一种情况,如果是多路(new多个AudioTrack线程)mix混音的情况,就是MixerThread进行软件混音mixer,然后在mixer算法计算来增大/减少PCM采样点来控制音量,
2.第二种方式,控制DAC硬件的增益,主要用在了DirectOutputThread,中,因为DirectOutputThread只有一路音频,直接写入HAL层,直接写入硬件的,所以需要直接调用硬件DAC芯片的控制接口来调整音量

1,音量调整场景
android 音量调整,可以使用两种方式: 
软件mixer的时候修改PCM data  
控制DAC硬件的增益

第一种情况,如果是多路mix的情况,就是MixerThread进行软件mixer,然后在mixer计算的时候来缩放PCM data,
首先,JNI层调用了AudioFlinger::setStreamVolume。
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来继续设置音量
void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
{
    Mutex::Autolock _l(mLock);
    mStreamTypes[stream].volume = value;//把音量数据存起来
    broadcast_l();
}
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函数
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来处理音量
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中的函数
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芯片中
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总线写入命令
关于驱动的流程我们在这里不展开讲,后续会单独讲解

android audio 音量设置分析相关推荐

  1. Android Audio音量设置原理流程分析

    Android Audio音量设置原理流程分析 简介 本篇文章主要介绍Android音量设置从App应用层到framework层执行流程,以及相关的细节和原理分析,建议在阅读此文章前去看博主的混音理论 ...

  2. android 声卡 音量控制,android audio 音量调节

    这次的分析是从setting设置开始,进入声音设置,然后进入音量设置! 先上传上来,后期进行整理吧 调用流程: -------------------------------------------- ...

  3. Android Audio音量保存

    1.AudioService初始化时会从Settings数据库中读取保存的各个device的值暂存并设置. createStreamStates() int numStreamTypes = Audi ...

  4. Android手机音量设置相关

    Android中手机声音调节步骤: a.通过系统服务获得声音管理器: AudioManager audioManager = (AudioManager)getSystemService(Servic ...

  5. Android 音频源码分析——音量调节流程

    源码分析基于android9.0 一.声音类型 对于大多数手机用户来说,操作手机音量按键可以看到,声音类型分为四种:媒体.铃声.闹钟.通话,但是其系统内部则分为十几种类型. 声⾳类型用来区分不同播放用 ...

  6. Android x86启动后拖动系统音量设置

    基于Android x86 android 9. 1.进入系统音量设置(系统 app)中,拖动媒体音量控制条控件,可以输出声音=>ok dumpsys audio: 可以看到player有sta ...

  7. Android audio音频流数据异常问题分析

    一.背景 在 Android 系统的开发过程当中,音频异常问题通常有如下几类,无声,调节不了声音,爆音,声音卡顿,声音效果异常(忽大忽小,低音缺失等)等.尤其声音效果这部分问题通常从日志上信息量较少, ...

  8. Android调节音量分析

    音量调节接口 我们知道Android Audio的音量调节接口是通过AudioManager的setStreamVolume实现的,这个函数的实现在AudioService的setStreamVolu ...

  9. Android Audio代码分析8 - AudioHardwareALSA::openOutputStream函数

    发现以前写的东西,对调用函数的展开放在了函数的前面,导致不方便找到原来代码及设置的函数参数. 以后打算稍作改动,把对被调函数的展开放在原代码的后面,这样看起来应该方便些. 闲言少叙,跳入代码. 前两天 ...

最新文章

  1. hdu 1788 Chinese remainder theorem again 【crt的具体过程】
  2. 回文串判定_JAVA
  3. 一个Demo学会用Android兼容包新控件
  4. 数据结构笔记(二十)--二叉树的存储
  5. 使用Flutter开发一个仿微信飞机大战游戏
  6. 现代电工技术实训考核装置
  7. 【转】HDMI视频分配器的功能及原理
  8. linux~tar压缩文件夹到指定文件夹 --- 相对路径
  9. Cisco QOS之LLQ
  10. KK 的99 条额外的建议[翻译]#yyds干货盘点#
  11. 高斯投影坐标计算例题_测量学高斯投影已知横坐标如何求在第几度带投影计算而得的?例如:...-y坐标的自然值怎么算-数学-莫囤料同学...
  12. 数学模型参考文献的格式
  13. ubuntu NFS SCP SFTP
  14. 《面试无忧》--DCL单例模式为什么要用volatile修饰?
  15. 西南大学统考英语计算机有答案吗,西南大学1806课程[0002]《英语》机考A卷答案参考...
  16. ARP代理(Proxy ARP)
  17. springboot校园二手交易市场网站毕业设计-附源码071008
  18. 尤登弘—中小企业最佳赢利管理模式
  19. WinForm DataGridView实时更新表格数据
  20. 炉石服务器请求超时何时才能修复,炉石传说登陆提示请求已超时怎么办_炉石传说登陆提示请求已超时解决方法_3DM网游...

热门文章

  1. jenkins + Git 搭建持续集成环境
  2. php : 匿名函数(闭包) [一]
  3. CentOS 6.6 安装 Node.js
  4. 从未停止!OpenGL的版本历史和发展
  5. 提高 Web Service 数据传输效率的基本方法
  6. 用GibbsLDA做Topic Modeling
  7. MySQL更新行和插入列的语句
  8. iOS 15 中的 Swift 和 SwiftUI
  9. 《How to Use the TimeDistributed Layer for Long Short-Term Memory Networks in Python》学习笔记
  10. 建立一个vs+qt打开系统摄像头的程序