OpenHarmony3.1 Release版本新增按键音量调节功能,如下图:
音量调节通俗的理解为按下按键,触发中断,中断处理函数中完成音量的调节。
那么在OpenHarmony Audio Service中是怎么完成的呢?
1.按键的注册,按键作为input模块,被多模输入子系统管理。
在Audio子系统中,AudioPolicy进程加载了多模子系统,并订阅的按键事件处理。根据按键的操作,触发音量调节接口,并通过PulseAudio调用HDI的接口,HDI接口通过Audio Dirver Model完成音量的调节。
下文将展开描述各个模块具体的执行流程。
Key输入与多模输入
1.KEY的注册。
KEY信息配置与节点配置,见vendor/hihope/rk3568/hdf_config/khdf/input/input_config.hcs,通过RegisterKeyDevice将Vol+/Vol-注册到HDF_KEY模块中,由input统一管理。
C
keyConfig {
keyList = ["power", "VolUp", "VolDown", "Up", "Down", "Left", "Right"];
keyInfoList {
key1 {
match_attr = "key_device0";
/* 0:touch 1:key 2:keyboard 3:mouse 4:button 5:crown 6:encoder */
inputType = 1;
keyName = "power";
gpioNum = 1;
irqFlag = 3;
debounceTime = 80;
}
key2 {
keyName = "volUp";
gpioNum = 31;
irqFlag = 1;
debounceTime = 80;
}
key3 {
keyName = "volDown";
gpioNum = 32;
irqFlag = 1;
debounceTime = 80;
}
}
}
在openHarmony中KEY与多模输入的简单关系图。
多模输入与AudioService如何联系
结合音频组件架构图,AudioPolicy主要完成Audio设备、音量等管理工作。
++
AudioPolicyServer完成依赖模块的加载,音量按键的调节与多模输入模块相关。
void AudioPolicyServer::OnAddSystemAbility(int32_t systemAbilityId, const std::string& deviceId)
{
switch (systemAbilityId) {
case MULTIMODAL_INPUT_SERVICE_ID: // 3101
......
SubscribeKeyEvents();
break;
......
}
}
2.SubscribeKeyEvents接口完成Vol+/Vol-按键事件的订阅,当前Input事件触发后会执行对应的动作。
C++
void AudioPolicyServer::SubscribeKeyEvents()
{
// 按键信息配置
MMI::InputManager *im = MMI::InputManager::GetInstance();
std::set<int32_t> preKeys;
std::shared_ptr<OHOS::MMI::KeyOption> keyOption_down = std::make_shared<OHOS::MMI::KeyOption>();
keyOption_down->SetPreKeys(preKeys);
keyOption_down->SetFinalKey(OHOS::MMI::KeyEvent::KEYCODE_VOLUME_DOWN);
keyOption_down->SetFinalKeyDown(true);
keyOption_down->SetFinalKeyDownDuration(0);
// key触发的事件
im->SubscribeKeyEvent(keyOption_down, [=](std::shared_ptr<MMI::KeyEvent> keyEventCallBack) {
std::lock_guard<std::mutex> lock(volumeKeyEventMutex_);
AudioStreamType streamInFocus = GetStreamInFocus();
if (streamInFocus == AudioStreamType::STREAM_DEFAULT) {
streamInFocus = AudioStreamType::STREAM_MUSIC;
}
// 获取当前音量
float currentVolume = GetStreamVolume(streamInFocus);
if (ConvertVolumeToInt(currentVolume) <= MIN_VOLUME_LEVEL) {
for (auto it = volumeChangeCbsMap_.begin(); it != volumeChangeCbsMap_.end(); ++it) {
std::shared_ptr<VolumeKeyEventCallback> volumeChangeCb = it->second;
if (volumeChangeCb == nullptr) {
MEDIA_ERR_LOG("volumeChangeCb: nullptr for client : %{public}d", it->first);
continue;
}
volumeChangeCb->OnVolumeKeyEvent(streamInFocus, MIN_VOLUME_LEVEL, true);
}
return;
}
// 设置音量
SetStreamVolume(streamInFocus, currentVolume-GetVolumeFactor(), true);
});
Audio Service如何进行音量设置
1.接着上述订阅事件,AudioPolicyServer通过调用AudioPolicyService类,AudioPolicyService继而调用AudioAdapterManager::SetStreamVolume。
此接口中,通过完成WriteVolumeToKvStore音量值的保存(kv:Key,value),继而调用AudioServiceAdapter::mAudioServiceAdapter->SetVolume(streamType, volume);进行音量设置。
2.AudioServiceAdapter::SetVolume接口。
PulseAudio作为Audio服务,SetVolume通过调用PulseAudio的接口完成音量设置。
C++
int32_t PulseAudioServiceAdapterImpl::SetVolume(AudioStreamType streamType, float volume)
{
......
pa_threaded_mainloop_lock(mMainLoop);
pa_operation *operation = pa_context_get_sink_input_info_list(mContext,
PulseAudioServiceAdapterImpl::PaGetSinkInputInfoVolumeCb, reinterpret_cast<void*>(userData.get()));
......
return SUCCESS;
}
此接口中PaGetSinkInputInfoVolumeCb作为回调函数,通过pa_context_set_sink_input_volume接口完成音量设置的具体工作。
C++
void PulseAudioServiceAdapterImpl::PaGetSinkInputInfoVolumeCb(pa_context *c, const pa_sink_input_info *i, int eol,void *userdata)
{
......
pa_cvolume cv = i->volume;
uint32_t volume = pa_sw_volume_from_linear(vol);
pa_cvolume_set(&cv, i->channel_map.channels, volume);
pa_operation_unref(pa_context_set_sink_input_volume(c, i->index, &cv, NULL, NULL));
......
return;
}
上述类之间的关系见图,如下:
pulseaudio如何与HDI接口对接
1.module-hdi-sink/module-hdi-source(foundation/multimedia/audio_standard/frameworks/native/pulseaudio/src/modules/hdi)作为PulseAudio的module,系统启动过程被audiopolicy进程加载启动。
2.renderer_sink_adapter(foundation/multimedia/audio_standard/frameworks/native/audiorenderer/src/renderer_sink_adapter.c) 作为hdi-sink和renderer之间的适配层,完成接口封装与定义。
C++
struct RendererSinkAdapter {
int32_t (*RendererSinkInit)(const SinkAttr *attr);
void (*RendererSinkDeInit)(void);
int32_t (*RendererSinkStart)(void);
int32_t (*RendererSinkStop)(void);
int32_t (*RendererRenderFrame)(char *data, uint64_t len, uint64_t *writeLen);
int32_t (*RendererSinkSetVolume)(float left, float right);
int32_t (*RendererSinkGetLatency)(uint32_t *latency);
};
其中RendererSinkSetVolume接口完成音量设置,LoadSinkAdapter接口根据不同的设备类型,给出不同的接口实现。
C++
int32_t LoadSinkAdapter(const char *device, struct RendererSinkAdapter **sinkAdapter)
{
......
if (!strcmp(device, g_deviceClassPrimary)) {
MEDIA_INFO_LOG("%{public}s: primary device", __func__);
......
adapter->RendererSinkSetVolume = AudioRendererSinkSetVolume;
......
g_deviceClass = CLASS_TYPE_PRIMARY;
} else if (!strcmp(device, g_deviceClassA2Dp)) {
MEDIA_INFO_LOG("%{public}s: a2dp device", __func__);
......
adapter->RendererSinkSetVolume = BluetoothRendererSinkSetVolume;
......
g_deviceClass = CLASS_TYPE_A2DP;
} else {
MEDIA_ERR_LOG("%{public}s: Device not supported", __func__);
free(adapter);
return ERROR;
}
......
}
3.RendererSinkSetVolume如何与HDI接口对接。
以primary device为例,RendererSinkSetVolume对应的实现为AudioRendererSinkSetVolume,调用g_audioRendrSinkInstance->SetVolume,即AudioRendererSink::SetVolume接口。
C++
int32_t AudioRendererSink::SetVolume(float left, float right)
{
......
ret = audioRender_->volume.SetVolume(reinterpret_cast<AudioHandle>(audioRender_), volume);
if (ret) {
MEDIA_ERR_LOG("AudioRendererSink::Set volume failed!");
}
return ret;
}
audioRender的定义为struct AudioRender *audioRender,AudioRender即为HDI接口,其定义参考(drivers/peripheral/audio/interfaces/include/audio_render.h)。
HDI如何完成音量设置,可以参考Audio HDI的测试用例,本文将不描述(*drivers/peripheral/audio/test/systemtest/hdi/render/src/audio_hdirender_volume_test.cpp)。