android audio 音量设置分析
16lz
2021-01-24
audiod 中经常遇到的场景是音量调整与输出设备的切换,下面两篇文章 针对这两个场景分别分析一下
1,音量调整场景
android 音量调整,可以使用两种方式:
软件mixer的时候修改PCM data
控制DAC硬件的增益
第一种情况,如果是多路mix的情况,就是MixerThread进行软件mixer,然后在mixer计算的时候来缩放PCM data,
首先,JNI层调用了AudioFlinger::setStreamVolume。
我们介绍过在audioMixer的实际操作函数是track__16BitsStereo这种函数,
对track__16BitsStereo的分析中,我们可以看到根据音量对PCM data进行实际的缩放
不再继续赘述了。
对于第二种方式,控制DAC硬件的增益,主要用在了DirectOutputThread,中,因为DirectOutputThread只有一路音频,直接写入HAL层,直接写入硬件的,
所以需要直接调用硬件DAC芯片的控制接口来调整音量。
其主要流程如下:
和mixerThread的流程一样,上层在调用了AudioFlinger::setStreamVolume之后,会调用prepareTracks_l函数
写入驱动
由于商业机密,HAL层代码不能贴出来。
到了内核中,则是按照以下调用序列,最终通过IIC总线,将音量控制命令写入了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
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(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_usersnd_ctl_elem_write_usersnd_ctl_elem_writewm8523_controlssnd_soc_put_volswsnd_soc_update_bits_locked
IIC总线写入命令 关于驱动的流程我们在这里不展开讲,后续会单独讲解
更多相关文章
- C语言函数的递归(上)
- Android(安卓)启动流程
- 【Android应用实例之五】调节播放器音量——AudioManager的应用
- Android(安卓)Framework 深入浅出-HAL, Binder, View System 与
- 深入理解zygote——1(代码源于GooGle)
- Android(安卓)4.1.2系统添加重启功能
- Android(安卓)NDK环境搭建和开发入门
- 观摩Android最高权力的Context通用性接口
- Android世界的Swift - Kotlin语言