3. 实现最小音频HAL模块骨架

实现最小音频HAL模块骨架 学习卡片

任务说明

在音频HAL实现中,audio_hw_device结构体包含一个函数指针get_next_device,用于遍历系统中所有音频设备。本案例实现该函数,使其能够枚举系统中的音频设备(如扬声器、耳机、麦克风等),并为每个设备返回完整的描述信息(设备类型、设备ID、设备名称、采样率范围、通道掩码等)。

核心数据结构

// 设备描述结构体
typedef struct audio_device_description {
    char device_id[32];          // 设备唯一标识
    char device_name[64];        // 设备名称
    audio_devices_t type;        // 设备类型(扬声器/耳机/麦克风等)
    uint32_t sample_rates;       // 支持的采样率掩码
    uint32_t channel_mask;       // 支持的通道掩码
    uint32_t formats;            // 支持的音频格式掩码
} audio_device_description_t;

// 设备节点(链表节点)
typedef struct audio_device_node {
    audio_device_description_t desc;
    struct audio_device_node *next;
} audio_device_node_t;

// HAL设备结构体(简化)
typedef struct audio_hw_device {
    // ... 其他成员
    audio_device_node_t *device_list;   // 设备链表头
    audio_device_node_t *current;       // 当前遍历位置
    int (*get_next_device)(struct audio_hw_device *dev, audio_device_description_t *desc);
} audio_hw_device_t;

实现代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

// 音频设备类型定义(简化)
typedef enum {
    AUDIO_DEVICE_OUT_SPEAKER = 0x2,
    AUDIO_DEVICE_OUT_WIRED_HEADSET = 0x8,
    AUDIO_DEVICE_IN_BUILTIN_MIC = 0x10000,
    AUDIO_DEVICE_IN_WIRED_HEADSET = 0x20000
} audio_devices_t;

// 采样率掩码
#define SAMPLE_RATE_44100  (1 << 0)
#define SAMPLE_RATE_48000  (1 << 1)

// 通道掩码
#define CHANNEL_MONO  0x1
#define CHANNEL_STEREO 0x3

// 格式掩码
#define FORMAT_PCM_16BIT 0x1
#define FORMAT_PCM_24BIT 0x2

// 设备描述结构体
typedef struct audio_device_description {
    char device_id[32];
    char device_name[64];
    audio_devices_t type;
    uint32_t sample_rates;
    uint32_t channel_mask;
    uint32_t formats;
} audio_device_description_t;

// 设备节点
typedef struct audio_device_node {
    audio_device_description_t desc;
    struct audio_device_node *next;
} audio_device_node_t;

// HAL设备结构体
typedef struct audio_hw_device {
    audio_device_node_t *device_list;
    audio_device_node_t *current;
    int (*get_next_device)(struct audio_hw_device *dev, audio_device_description_t *desc);
} audio_hw_device_t;

// 创建设备节点
static audio_device_node_t* create_device_node(
    const char *id, const char *name, audio_devices_t type,
    uint32_t rates, uint32_t channels, uint32_t formats) {
    audio_device_node_t *node = (audio_device_node_t*)calloc(1, sizeof(audio_device_node_t));
    if (!node) return NULL;
    strncpy(node->desc.device_id, id, sizeof(node->desc.device_id) - 1);
    strncpy(node->desc.device_name, name, sizeof(node->desc.device_name) - 1);
    node->desc.type = type;
    node->desc.sample_rates = rates;
    node->desc.channel_mask = channels;
    node->desc.formats = formats;
    node->next = NULL;
    return node;
}

// 初始化设备列表(模拟系统探测)
static void init_device_list(audio_hw_device_t *hw_dev) {
    audio_device_node_t *speaker = create_device_node(
        "out_speaker", "Builtin Speaker", AUDIO_DEVICE_OUT_SPEAKER,
        SAMPLE_RATE_44100 | SAMPLE_RATE_48000, CHANNEL_STEREO, FORMAT_PCM_16BIT);
    audio_device_node_t *headset_out = create_device_node(
        "out_headset", "Wired Headset", AUDIO_DEVICE_OUT_WIRED_HEADSET,
        SAMPLE_RATE_44100 | SAMPLE_RATE_48000, CHANNEL_STEREO, FORMAT_PCM_16BIT | FORMAT_PCM_24BIT);
    audio_device_node_t *mic = create_device_node(
        "in_builtin_mic", "Builtin Microphone", AUDIO_DEVICE_IN_BUILTIN_MIC,
        SAMPLE_RATE_44100 | SAMPLE_RATE_48000, CHANNEL_MONO, FORMAT_PCM_16BIT);
    audio_device_node_t *headset_in = create_device_node(
        "in_headset_mic", "Headset Microphone", AUDIO_DEVICE_IN_WIRED_HEADSET,
        SAMPLE_RATE_44100 | SAMPLE_RATE_48000, CHANNEL_MONO, FORMAT_PCM_16BIT);

    // 构建链表:扬声器 -> 耳机输出 -> 内置麦克风 -> 耳机麦克风
    hw_dev->device_list = speaker;
    speaker->next = headset_out;
    headset_out->next = mic;
    mic->next = headset_in;
    hw_dev->current = hw_dev->device_list;
}

// get_next_device 实现
static int get_next_device_impl(audio_hw_device_t *hw_dev, audio_device_description_t *desc) {
    if (!hw_dev || !desc) return -1;
    if (!hw_dev->current) return 0;  // 遍历结束

    // 拷贝当前设备描述
    *desc = hw_dev->current->desc;
    hw_dev->current = hw_dev->current->next;
    return 1;  // 返回1表示成功获取一个设备
}

// 释放设备链表
static void free_device_list(audio_hw_device_t *hw_dev) {
    audio_device_node_t *node = hw_dev->device_list;
    while (node) {
        audio_device_node_t *next = node->next;
        free(node);
        node = next;
    }
    hw_dev->device_list = NULL;
    hw_dev->current = NULL;
}

// 主函数:演示枚举过程
int main() {
    audio_hw_device_t hw_dev;
    memset(&hw_dev, 0, sizeof(hw_dev));
    hw_dev.get_next_device = get_next_device_impl;

    // 初始化设备列表
    init_device_list(&hw_dev);

    // 枚举所有设备
    audio_device_description_t desc;
    int count = 0;
    printf("=== Audio Device Enumeration ===\n");
    while (hw_dev.get_next_device(&hw_dev, &desc) > 0) {
        count++;
        printf("Device[%d]:\n", count);
        printf("  ID:     %s\n", desc.device_id);
        printf("  Name:   %s\n", desc.device_name);
        printf("  Type:   0x%x\n", desc.type);
        printf("  Rates:  %s%s\n",
               (desc.sample_rates & SAMPLE_RATE_44100) ? "44100 " : "",
               (desc.sample_rates & SAMPLE_RATE_48000) ? "48000" : "");
        printf("  ChMask: 0x%x\n", desc.channel_mask);
        printf("  Format: 0x%x\n\n", desc.formats);
    }
    printf("Total devices: %d\n", count);

    // 重置遍历位置(模拟重新枚举)
    hw_dev.current = hw_dev.device_list;
    printf("\n=== Second Enumeration (reset) ===\n");
    count = 0;
    while (hw_dev.get_next_device(&hw_dev, &desc) > 0) {
        count++;
        printf("Device[%d]: %s (%s)\n", count, desc.device_name, desc.device_id);
    }
    printf("Total devices: %d\n", count);

    // 清理
    free_device_list(&hw_dev);
    return 0;
}

关键实现说明

  • 链表结构:使用单向链表存储设备描述,device_list指向链表头,current记录当前遍历位置。
  • 遍历语义:get_next_device每次调用返回当前设备描述并将current后移;返回0表示遍历结束,返回-1表示参数错误。
  • 重置机制:将current重新指向链表头即可开始新一轮枚举,符合HAL中重新扫描设备的需求。
  • 设备描述:包含设备ID、名称、类型、采样率、通道掩码和格式掩码,覆盖了HAL层设备描述的核心字段。

编译与运行

gcc -o audio_enum audio_enum.c
./audio_enum

预期输出

=== Audio Device Enumeration ===
Device[1]:
  ID:     out_speaker
  Name:   Builtin Speaker
  Type:   0x2
  Rates:  44100 48000
  ChMask: 0x3
  Format: 0x1

Device[2]:
  ID:     out_headset
  Name:   Wired Headset
  Type:   0x8
  Rates:  44100 48000
  ChMask: 0x3
  Format: 0x3

Device[3]:
  ID:     in_builtin_mic
  Name:   Builtin Microphone
  Type:   0x10000
  Rates:  44100 48000
  ChMask: 0x1
  Format: 0x1

Device[4]:
  ID:     in_headset_mic
  Name:   Headset Microphone
  Type:   0x20000
  Rates:  44100 48000
  ChMask: 0x1
  Format: 0x1

Total devices: 4

=== Second Enumeration (reset) ===
Device[1]: Builtin Speaker (out_speaker)
Device[2]: Wired Headset (out_headset)
Device[3]: Builtin Microphone (in_builtin_mic)
Device[4]: Headset Microphone (in_headset_mic)
Total devices: 4

验证方法

运行程序后,观察输出是否包含4个设备且字段完整。验证要点:

  1. 第一次枚举输出4个设备,每个设备包含ID、名称、类型、采样率、通道掩码和格式掩码;
  2. 第二次枚举(重置后)同样输出4个设备,证明遍历位置可重置;
  3. 设备顺序与链表构建顺序一致(扬声器→耳机输出→内置麦克风→耳机麦克风);
  4. 当current为NULL时,get_next_device返回0,循环正常终止。
get_next_device 遍历机制 Node 1 out_speaker Builtin Speaker type: 0x2 Node 2 out_headset Wired Headset type: 0x8 Node 3 in_builtin_mic Builtin Mic type: 0x10000 Node 4 in_headset_mic Headset Mic type: 0x20000 → NULL current 指针 调用流程 ① 调用 get_next_device(dev, desc) current 指向 Node 1 ② 拷贝 desc = current->desc 返回 1(成功) ③ current = current->next 指向 Node 2 返回 1:成功获取设备 | 返回 0:遍历结束(current == NULL) | 返回 -1:参数错误

判定标准

  • 程序编译无错误,运行后输出4个设备的完整描述信息;
  • 设备ID、名称、类型与init_device_list中初始化数据完全一致;
  • 第二次枚举输出与第一次相同,证明current重置机制有效;
  • 遍历结束后再次调用get_next_device返回0,不会越界访问。