1)概述

1、回顾HAL、tinyalsa与linux driver的关系

2、与AudioFlinger的关系

3、

1、如何判断当前平台用的是哪个库?
android\hardware\libhardware\modules\audio
android\hardware\libhardware_legacy\audio\audio_hw_hal.cpp
可尝试注入错误代码,单编 验证一下 > 但编了也不一定会用!

2、hal的作用?隐藏了什么细节,还是说只是为了符合Android框架而写?
>>播放数据之前的,设置的步骤和参数就是厂家要保护的内容

3、HAL如何对接tinyalsa?
把tinyalsa当做一个库接口使用即可

2)HAL的打开流程

1、由AudioFlinger负责加载
android\frameworks\av\services\audioflinger\AudioFlinger.cpp
audio_module_handle_t AudioFlinger::loadHwModule(const char *name) //name为动态库的名字

2、数据结构:
/android/frameworks/av/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h 
//接口与audio_hw_hal.cpp一一对应

android\frameworks\av\services\audioflinger\AudioHwDevice.h //存放device
class AudioHwDevice {
private:
    const audio_module_handle_t mHandle;
    const char * const          mModuleName;
    sp<DeviceHalInterface>      mHwDevice;
    const Flags                 mFlags;
};

3、以aidl / hidl为例,传统hal实现则直接使用hw_get_module得到对应的库
AudioHwDevice* AudioFlinger::loadHwModule_l(const char *name)
mDevicesFactoryHal = DevicesFactoryHalInterface::create(); //根据版本选择aidl / hidl进程

4、
android\frameworks\av\media\libaudiohal\FactoryHal.cpp
void *createPreferredImpl(bool isDevice) {
if (createHalService(std::max(*ifaceVersionIt, *siblingVersionIt), isDevice,
                             &rawInterface)) {
            return rawInterface;
        }
}

mDevicesFactoryHal->openDevice(name, &dev);
--AServiceManager_waitForService(serviceName.c_str()); 

5、厂商实现的Audio aidl hal service在哪里?
android\hardware\interfaces\audio\aidl\default\main.cpp
原本传统hal库由audio policy解析audio_policy_config而来,Audio aidl hal service也要去解析audio_policy_config去dlopen库?按理来说是的

3)HAL库的实现(Qualcomm)

1、目录介绍:
Qualcomm开源的hal实现
android\hardware\qcom\audio\hal //HAL新架构
android\hardware\qcom\audio\legacy\alsa_sound //HAL旧架构
android\hardware\qcom\audio\legacy\libalsa-intf //alsa-lib

2、由于Android系统的发展变化(这些混合的代码 实在让人看着难受,还费时间去对比筛选),存在多种架构
1)hal实现分为旧架构和新架构,官方实现的demo代码对应路径,厂商需要根据这个去填充自家代码实现
旧架构:android\hardware\libhardware_legacy
新架构:android\hardware\libhardware\modules\audio

2)alsa-lib与tinyalsa
在Android 4.0之前是使用这alsa-lib接口,之后简化演变成现在的tinyalsa
qcom实现的alsa-lib : android\hardware\qcom\audio\legacy\libalsa-intf
tinyalsa :android\external\tinyalsa

本文以HAL旧架构 + tinyalsa为例子分析hal实现

3、通信模型大致如下
AudioFlinger -> aidl -> hal -> hal库(qualcomm)  -> tinyalsa -> linux driver

4、常见音效:
音质:acoustics
音频后处理效果:Post Processing Effects
强化低频响应:Bass Boost
动态范围压缩:Dynamic Range Compression
虚拟环绕声:Virtualizer
均衡器:EQ

5、submix
虚拟设备,实现内部音频流混合与重定向,支持屏幕录制、音频转发等高级功能
AudioUsbALSA.cpp -> 外接USB声卡

6、基本代码分析
1)HAL入口函数 - 谁来调用?AudioFlinger
static int legacy_adev_open(const hw_module_t* module, const char* name,
                            hw_device_t** device)
2)
static struct hw_module_methods_t legacy_audio_module_methods = {
        open: legacy_adev_open
};

struct legacy_audio_module HAL_MODULE_INFO_SYM = {
    module: {
        common: {
            tag: HARDWARE_MODULE_TAG,
            module_api_version: AUDIO_MODULE_API_VERSION_0_1,
            hal_api_version: HARDWARE_HAL_API_VERSION,
            id: AUDIO_HARDWARE_MODULE_ID,
            name: "LEGACY Audio HW HAL",
            author: "The Android Open Source Project",
            methods: &legacy_audio_module_methods,
            dso : NULL,
            reserved : {0},
        },
    },
};

3)hwif 即厂商实现的 对接hal接口
struct legacy_stream_out {
    struct audio_stream_out stream;
    AudioStreamOut *legacy_out;
};

struct legacy_stream_in {
    struct audio_stream_in stream;
    AudioStreamIn *legacy_in;
};

static int adev_open_output_stream()
{
    out->legacy_out = ladev->hwif->openOutputStreamWithFlags(devices, flags,
                                                    (int *) &config->format,
                                                    &raw_channel_mask,
                                                    &config->sample_rate, &status);
}

4)AudioHardwareALSA()构造函数中
打开库ro.hardware.alsa.default
AudioHardwareALSA()
{
    hw_device_t* device;
        err = module->methods->open(module, ALSA_HARDWARE_NAME, &device);
        if (err == 0) {
            mALSADevice = (alsa_device_t *)device;
            mALSADevice->init(mALSADevice, mDeviceList);
        }
}

ssize_t AudioStreamOutALSA::write(const void *buffer, size_t bytes)
{
    n = pcm_write(mHandle->handle, (char *)buffer + sent, period_size);
}

5)
#define HAL_MODULE_INFO_SYM HWI  //hal module info
#define HAL_MODULE_INFO_SYM_AS_STR "HMI" //

#define ALSA_HARDWARE_MODULE_ID "alsa"
#define ALSA_HARDWARE_NAME      "alsa"

6)alsa_handle_t
typedef List < alsa_handle_t > ALSAHandleList;

struct alsa_handle_t {
   alsa_device_t *module;
   uint32_t devices;
   strcut pcm *handle;
   snd_pcm_format_t format;
}

struct alsa_device_t {
    hw_device_t common;
    status_t (*init)(alsa_device_t *, ALSAHandleList &);
    status_t (*open)(alsa_handle_t *);
    ...
    void (*setVoiceVolume)(int);
}

typedef struct hw_deivce_t {
    struct hw_module_t* module;
    int (*close)(struct hw_device_t* device);
} hw_device_t;

4)tinyalsa

1、tinyalsa这套框架带来了什么?
>>操作linux driver需要open/read/write,tinyalsa根据asoc协议封装对应的操作,提供统一接口给到hal,hal只需少量参数即可操作pcm设备节点,实现功能

2、测试工具tinyplay/tinycap/tinymix
源码位置:
android\external\tinyalsa\tinyplay.c
板卡位置
/system/bin/tinyplay  //播放工具
/system/bin/tinycap //录音工具
/system/bin/tinymix //控制工具(设置音量等操作),注意不是mix混音

示例:tinyplay /data/M1F1-int16WE-AFsp.wav -D 1 -d 1

3、音频格式:
1)PCM与RAW介绍 : https://blog.csdn.net/weixin_42654603/article/details/143757958
2)tiny只支持wav格式?> 仅支持 PCM(原始数据格式)/ RAW(直接从音频源-如MIC,不经过编码和压缩的原始数据)格式
3)*.wav :Waveform audio File Format (windows系统的标准音频格式之一) - 不涉及加解密,是PCM流 储存的文件格式(在PCM流的基础上加一些控制信息)
4)常说的DOlby音效是什么格式?一般采用AC-3编码技术
5)WACE Sample Files下载 : https://blog.csdn.net/touzani/article/details/1655328

4、在TV上,digital output选择PCM和RAW 又分别代表什么?
PCM:会将音频数据 转换 PCM流,兼容绝大多数音频设备;
RAM:不会处理,直接输出给音频设备,音频设备要求很高;


5、tinymix的使用
1)查看tinymix可控的寄存器列表
root@# tinymix
Mixer name: 'audiocodec'
Number of controls: 16
ctl type num name          value
1   INT  1  "digital volume" 0
2   INT  1  "LINEIN to output mixer gain control" 3
3   BOOL 1  "LINEOUT Switch" On
...

2)设置音量
tinymix "LINEOUT volume" "2"

5)数据结构

1、module与device
在这里插入图片描述

1)qcom_audio_device与audio_hw_device的"继承"用法

这样设计的目的是什么?为什么转换来转换去?
struct qcom_audio_device {
    struct audio_hw_device device; //向上层提供标准接口
    struct AudioHardwareInterface *hwif; //向下是指向厂商实现的接口
};

先来看看如何使用
static int qcom_adev_open(const hw_module_t* module, const char* name,
                            hw_device_t** device)
{
    ...
    qadev->device.init_check = adev_init_check;
    qadev->hwif = createAudioHardware();
    ...
    *device = &qadev->device.common; //将构造好的device返回给上层调用者保存
}

static int adev_init_check(const struct audio_hw_device *dev)
{
    const struct qcom_audio_device *qadev = to_cladev(dev); //将上层传递下来的device转换一下,相当于父子指针转换
    return qadev->hwif->initCheck(); //调用厂家实现的接口
}

小结:向上提供的接口需要是稳定的,具体的实现则由厂商自行设计,这样是灵活的
1)父子对象可以互相转换,对于上层和hal可以用同一个指针地址,不用额外设计对应关系;
2)如果不分开audio_hw_device、AudioHardwareInterface两个结构体,则厂商的灵活性会差些,反之厂商可以根据实际情况去调整,更加灵活;

2、stream音频流

在这里插入图片描述

1)HAL库的qcom_stream_out和qcom_stream_in类设计

struct qcom_stream_out {
    struct audio_stream_out stream; //向上提供接口
    AudioStreamOut *qcom_out; //指向厂家的接口 AudioStreamOutALSA
};

struct qcom_stream_in {
    struct audio_stream_in stream; //向上提供接口
    AudioStreamIn *qcom_in; //指向厂家的接口 AudioStreamInALSA
};

设计目的与qcom_audio_device同理

2)qcom_stream_out与AudioStreamOutALSA的继承""用法

1、out->qcom_out = qadev->hwif->openOutputStream(devices,..);
2、AudioStreamOut * AudioHardwareALSA::openOutputStream(){
AudioStreamOutALSA *out = 0;
...
return out;
}
先把父类"继承"过来,再将其它子类独有成员一一 赋值

3)类的层级关系设计如下
handle(module的容器) -> module -> device -> stream
device //代表一个音频设备
stream //由于音频流操作颇多,抽象出单独的类,负责控制音频流,播放,暂停等

3、AlSAMixer与ALSAControl
在这里插入图片描述

1)主要作用是读写设备节点“controlC0”,设置声卡参数

6)代码流程

在这里插入图片描述

2、上层三个步骤使用声卡播放

qcom_adev_open() //hal层的open,分配资源
adev_open_output_session() // 打开声卡设备,构造stream方法
qcom_stream_out->stream.write() //写声卡数据
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐