您的位置:首页 > 移动开发 > Android开发

android audio 音量设置分析

2016-02-20 19:56 585 查看
audiod 中经常遇到的场景是音量调整与输出设备的切换,下面两篇文章 针对这两个场景分别分析一下

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总线写入命令

关于驱动的流程我们在这里不展开讲,后续会单独讲解
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: