返回首页

LE Audio 学习笔记

概述

LE Audio 是 Bluetooth 5.2 引入的低功耗音频解决方案,支持广播音频、多音频流和 LC3 编解码器。

核心特性

1. 新增功能

特性描述
LC3 编解码器更高效的音频编解码器
广播音频一对多音频传输
立体声同步双声道
助听器无障碍支持
Auracast公共广播分享

2. 架构

┌─────────────────────────────────────────────┐
│              Application                     │
├─────────────────────────────────────────────┤
│         Audio Framework                      │
│  ┌─────────────┐  ┌─────────────┐          │
│  │ LC3 Encoder │  │ LC3 Decoder │          │
│  └─────────────┘  └─────────────┘          │
├─────────────────────────────────────────────┤
│         Audio Streams                        │
│  ┌─────────────┐  ┌─────────────┐          │
│  │  Isochronous│  │  CIS/BIS   │          │
│  │   Channel   │  │  Channel   │          │
│  └─────────────┘  └─────────────┘          │
├─────────────────────────────────────────────┤
│         GATT Services                        │
│  ┌─────────────┐  ┌─────────────┐          │
│  │   ASCS     │  │   PACS     │          │
│  └─────────────┘  └─────────────┘          │
├─────────────────────────────────────────────┤
│         Bluetooth 5.2+                      │
└─────────────────────────────────────────────┘

基本概念

1. 术语

术语描述
CISConnected Isochronous Stream
BISBroadcast Isochronous Stream
CIGConnected Isochronous Group
BIGBroadcast Isochronous Group
LC3Low Complexity Communication Codec
ASCSAudio Stream Control Service
PACSPublished Audio Capabilities Service
ASAudio Stream
BAPBasic Audio Profile

2. Audio Location

/* 音频位置定义 */
#define BT_AUDIO_LOCATION_FRONT_LEFT           BIT(0)
#define BT_AUDIO_LOCATION_FRONT_RIGHT          BIT(1)
#define BT_AUDIO_LOCATION_FRONT_CENTER         BIT(2)
#define BT_AUDIO_LOCATION_LOW_FREQ_EFFECTS_1   BIT(3)
#define BT_AUDIO_LOCATION_BACK_LEFT            BIT(4)
#define BT_AUDIO_LOCATION_BACK_RIGHT           BIT(5)
#define BT_AUDIO_LOCATION_BACK_CENTER          BIT(6)
#define BT_AUDIO_LOCATION_LOW_FREQ_EFFECTS_2   BIT(7)

3. Audio Context

/* 音频场景类型 */
#define BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED      BIT(0)
#define BT_AUDIO_CONTEXT_TYPE_CONVERSATIONAL  BIT(1)
#define BT_AUDIO_CONTEXT_TYPE_MEDIA            BIT(2)
#define BT_AUDIO_CONTEXT_TYPE_GAME             BIT(3)
#define BT_AUDIO_CONTEXT_TYPE_INSTRUCTIONAL    BIT(4)
#define BT_AUDIO_CONTEXT_TYPE_VOICE_ASSISTANTS  BIT(5)
#define BT_AUDIO_CONTEXT_TYPE_LIVE             BIT(6)
#define BT_AUDIO_CONTEXT_TYPE_SOUND_EFFECTS    BIT(7)

Kconfig 配置

1. 方向性理解

LE Audio 相关配置通常围绕这些能力展开:

CONFIG_BT=y
CONFIG_BT_AUDIO=y
CONFIG_BT_ISO=y

# 角色按实际场景选择
CONFIG_BT_BAP_UNICAST_CLIENT=y
# CONFIG_BT_BAP_UNICAST_SERVER=y
# CONFIG_BT_BAP_BROADCAST_SOURCE=y
# CONFIG_BT_BAP_BROADCAST_SINK=y

# 能力发布 / 流控制
CONFIG_BT_PACS=y
CONFIG_BT_ASCS=y

2. 关于具体参数

LC3 编解码器

1. 编解码参数

LC3 是 LE Audio 默认编解码器。工程里通常不会自己手写一整套“内部结构体定义”,而是直接使用 Zephyr 当前公开的 Bluetooth Audio API、preset 宏和 sample/application 的配置。

2. 编解码器能力

/* 编解码器能力结构 */
struct bt_audio_codec_cap {
    uint8_t id;
    uint16_t vendor_id;
    uint16_t vendor_codec_id;
    
    /* 支持的采样率 */
    uint32_t sample_frequencies;
    
    /* 支持的帧时长 */
    uint8_t frame_durations;
    
    /* 支持的声道配置 */
    uint8_t channel_counts;
    
    /* 每帧字节数范围 */
    uint16_t min_octets_per_frame;
    uint16_t max_octets_per_frame;
    
    /* 块 */
    uint8_t max_blocks_per_sdu;
};

/* 创建 LC3 能力 */
static struct bt_audio_codec_cap lc3_cap = {
    .id = BT_AUDIO_CODEC_LC3_ID,
    .sample_frequencies = BT_AUDIO_SAMPLE_RATE_16000 |
                          BT_AUDIO_SAMPLE_RATE_24000 |
                          BT_AUDIO_SAMPLE_RATE_48000,
    .frame_durations = BT_AUDIO_FRAME_DURATION_7500US |
                       BT_AUDIO_FRAME_DURATION_10000US,
    .channel_counts = 1 | 2,
    .min_octets_per_frame = 20,
    .max_octets_per_frame = 155,
    .max_blocks_per_sdu = 1,
};

Audio Stream

1. 流配置

音频流管理的关键点是:

2. 流状态

/* 流状态机 */
enum bt_bap_state {
    BT_BAP_STATE_IDLE,         /* 空闲 */
    BT_BAP_STATE_CONFIG,       /* 配置中 */
    BT_BAP_STATE_QOS,          /* QoS 配置中 */
    BT_BAP_STATE_ENABLING,     /* 启用中 */
    BT_BAP_STATE_STREAMING,    /* 传输中 */
    BT_BAP_STATE_PAUSING,      /* 暂停中 */
    BT_BAP_STATE_PAUSED,       /* 已暂停 */
    BT_BAP_STATE_DISABLING,    /* 禁用中 */
    BT_BAP_STATE_RELEASING,    /* 释放中 */
};

3. QoS 配置

/* ISO QoS 配置 */
struct bt_iso_qos {
    struct {
        uint16_t interval;    /* ISO 间隔 (1250us - 4s) */
        uint16_t latency;      /* 延迟 (10ms - 255ms) */
        uint8_t  sdu;          /* SDU 大小 */
        uint8_t  ft;           /* 填充数 */
        uint8_t  burst;        /* 突发数 */
    } cig;
    
    struct {
        uint16_t max_sdu;     /* 最大 SDU */
        uint8_t  rtn;         /* 重传次数 */
        uint8_t  max_latency; /* 最大延迟 */
    } cis;
};

/* 预设 QoS 配置 */
static const struct bt_iso_qos qos_preset = {
    .cig = {
        .interval = 10000,     /* 10ms */
        .latency = 10,         /* 10ms */
        .sdu = 80,             /* 80 字节 */
        .ft = 1,               /* 1 帧/子事件 */
        .burst = 1,            /* 1 突发 */
    },
    .cis = {
        .max_sdu = 80,
        .rtn = 2,
        .max_latency = 14,
    },
};

BLE Audio 服务

1. ASCS 服务

/* ASCS 特征值 UUID */
#define BT_UUID_ASCS_ASE      BT_UUID_128_ENCODE(0x2B3E, 0x1C5A, 0x3E4F, 0x9A2B, 0x8B6C, 0x4D5E, 0x7F8A, 0x9B0C)
#define BT_UUID_ASCS_CONTROL  BT_UUID_128_ENCODE(0x2B4E, 0x1C5A, 0x3E4F, 0x9A2B, 0x8B6C, 0x4D5E, 0x7F8A, 0x9B0C)

/* ASE 状态 */
struct bt_ascs_ase_state {
    enum {
        ASE_STATE_IDLE,        /* 空闲 */
        ASE_STATE_CFP,         /* Codec 配置待定 */
        ASE_STATE_CONFIG,      /* 配置中 */
        ASE_STATE_READY,       /* 就绪 */
        ASE_STATE_STREAMING,   /* 传输中 */
        ASE_STATE_RELEASING,   /* 释放中 */
    } state;
    
    uint8_t  ase_id;
    uint8_t  ase_state;
    uint16_t ase_state_control;
    struct bt_audio_codec_cfg codec;
    struct bt_iso_qos iso_qos;
};

/* ASCS 回调 */
static void ascs_discover_cb(struct bt_conn *conn, int err,
                            struct bt_ascs_ase *ase, uint8_t num)
{
    printk("Discovered %d ASEs\n", num);
}

static void ascs_state_cb(struct bt_ascs_ase *ase,
                         enum bt_ascs_ase_state state)
{
    printk("ASE %d state: %d\n", ase->id, state);
}

static struct bt_ascs_cb ascs_cbs = {
    .discover = ascs_discover_cb,
    .state = ascs_state_cb,
};

2. PACS 服务

/* PACS 服务 */
static const struct bt_audio_pac_record pac_record = {
    /* 麦克风能力 */
    .codec = &lc3_cap,
    .contexts = BT_AUDIO_CONTEXT_TYPE_CONVERSATIONAL,
    .meta = BT_AUDIO_META_NONE,
};

/* 发布能力 */
static int publish_capabilities(void)
{
    int err;
    
    err = bt_pacs_capability_register(BT_AUDIO_DIR_SOURCE,
                                     &pac_record);
    if (err) {
        printk("Failed to register capabilities: %d\n", err);
        return err;
    }
    
    printk("Capabilities registered\n");
    return 0;
}

3. 完整服务初始化

#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/audio/audio.h>
#include <zephyr/bluetooth/audio/ascs.h>
#include <zephyr/bluetooth/audio/pacs.h>
#include <zephyr/bluetooth/audio/bap.h>

/* Audio Stream */
static struct bt_bap_stream audio_stream;
static struct bt_bap_stream_ops stream_ops;

/* ASCS 回调 */
static void stream_started(struct bt_bap_stream *stream)
{
    printk("Audio stream started\n");
}

static void stream_stopped(struct bt_bap_stream *stream, int err)
{
    printk("Audio stream stopped: %d\n", err);
}

static void stream_recv(struct bt_bap_stream *stream,
                       const struct bt_audio_recv_info *info,
                       struct net_buf *buf)
{
    /* 处理接收的音频数据 */
    int16_t *pcm_data = (int16_t *)buf->data;
    size_t pcm_count = buf->len / sizeof(int16_t);
    
    /* 音频处理/播放 */
    audio_playback(pcm_data, pcm_count);
}

static void stream_released(struct bt_bap_stream *stream)
{
    printk("Audio stream released\n");
}

static struct bt_bap_stream_ops stream_ops = {
    .started = stream_started,
    .stopped = stream_stopped,
    .recv = stream_recv,
    .released = stream_released,
};

/* 初始化 Audio */
int audio_init(void)
{
    int err;
    
    /* 注册流回调 */
    bt_bap_stream_cb_register(&audio_stream, &stream_ops);
    
    /* 发布音频能力 */
    err = bt_pacs_set_location(BT_AUDIO_LOCATION_FRONT_LEFT |
                               BT_AUDIO_LOCATION_FRONT_RIGHT);
    if (err) {
        printk("Failed to set location: %d\n", err);
        return err;
    }
    
    /* 发布支持的上下文 */
    err = bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SOURCE,
                                         BT_AUDIO_CONTEXT_TYPE_MEDIA |
                                         BT_AUDIO_CONTEXT_TYPE_CONVERSATIONAL);
    if (err) {
        printk("Failed to set contexts: %d\n", err);
        return err;
    }
    
    printk("Audio initialized\n");
    return 0;
}

Audio 播放

1. 音频播放流程

┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  Audio     │───▶│  LC3       │───▶│  ISO       │
│  PCM Data  │    │  Encoder   │    │  Stream    │
└─────────────┘    └─────────────┘    └─────────────┘
                                            │
                                            ▼
                                    ┌─────────────┐
                                    │  Bluetooth │
                                    │  Radio     │
                                    └─────────────┘

2. 发送音频数据

/* 编码并发送音频 */
static int audio_send(struct bt_bap_stream *stream,
                     const int16_t *pcm_data,
                     size_t pcm_count)
{
    struct net_buf *buf;
    uint8_t encoded_data[200];
    size_t encoded_len;
    int err;
    
    /* LC3 编码 */
    err = lc3_encode(encoder, pcm_data, pcm_count,
                    encoded_data, &encoded_len);
    if (err) {
        printk("LC3 encode failed: %d\n", err);
        return err;
    }
    
    /* 分配缓冲区 */
    buf = net_buf_alloc(iso_pool, K_FOREVER);
    net_buf_add_mem(buf, encoded_data, encoded_len);
    
    /* 发送 */
    err = bt_bap_stream_send(stream, buf, seq_num++, 0);
    if (err) {
        printk("Failed to send audio: %d\n", err);
        net_buf_unref(buf);
        return err;
    }
    
    return 0;
}

/* 定时发送音频 */
static void audio_timer_handler(struct k_timer *timer)
{
    static uint16_t seq_num;
    
    /* 读取麦克风 */
    int16_t mic_data[128];
    size_t count = mic_read(mic_data, 128);
    
    /* 发送 */
    audio_send(&audio_stream, mic_data, count);
}

K_TIMER_DEFINE(audio_timer, audio_timer_handler, NULL);

麦克风

1. 麦克风配置

采集侧通常通过 I2S / audio pipeline 获取 PCM 数据,然后进入 LC3 编码和 ISO 发送链路。具体驱动细节与板卡和音频前端强相关,不建议把某个 i2s_read() 片段当通用 LE Audio 模板。

广播音频

1. 广播源

广播源常见流程:

  1. 配置广播参数和 subgroup/stream
  2. 创建 broadcast source
  3. 启动扩展广播
  4. 启动 BIS 音频流
  5. 停止并释放 source

2. 广播接收

广播接收常见流程:

  1. 扫描扩展广播
  2. 解析 BASE / 广播标识
  3. 创建 broadcast sink
  4. 同步 BIS
  5. 在 stream 回调中接收音频数据

立体声

1. 立体声流

/* 左右声道流 */
static struct bt_bap_stream stream_left;
static struct bt_bap_stream stream_right;

/* 立体声配置 */
static const struct bt_audio_codec_cfg stereo_codec = {
    .id = BT_AUDIO_CODEC_LC3_ID,
    .data = {
        LC3_SAMPLE_RATE, 2, 0x03, 0x00,  /* 48kHz */
        LC3_FRAME_DURATION, 1, 0x07,      /* 7.5ms */
        LC3_CHANNEL_COUNT, 1, 0x02,       /* 2 声道 */
    },
};

/* 发送立体声 */
static int send_stereo(const int16_t *left,
                      const int16_t *right,
                      size_t frames)
{
    int err;
    
    /* 交错数据 */
    int16_t interleaved[frames * 2];
    for (size_t i = 0; i < frames; i++) {
        interleaved[i * 2] = left[i];
        interleaved[i * 2 + 1] = right[i];
    }
    
    /* 发送到左右流 */
    err = audio_send(&stream_left, interleaved, frames);
    if (err) return err;
    
    err = audio_send(&stream_right, interleaved + frames, frames);
    if (err) return err;
    
    return 0;
}

Auracast

1. 公共广播

/* Auracast 广播参数 */
static const struct bt_audio_data auracast_adv_data[] = {
    BT_AUDIO_DATA(BT_AUDIO_DATA_URI, "urn:btlink:channel:public", 26),
    BT_AUDIO_DATA(BT_AUDIO_DATA_NAME, "Auracast Demo", 12),
};

/* 广播元数据 */
static const struct bt_audio_data auracast_meta[] = {
    BT_AUDIO_DATA(BT_AUDIO_DATA_VENDOR,
                  "\x01\x02\x03\x04", 4),
};

/* 创建 Auracast 广播 */
static int create_auracast(void)
{
    /* 类似广播源,但添加元数据 */
}

调试

1. 配置日志

CONFIG_BT_DEBUG_LOG=y
# 具体子模块调试选项以当前 Bluetooth Audio Kconfig 为准

2. nRF Tools

# nRF Connect for Mobile
# 扫描 LE Audio 设备
# 测试音频流
# 广播音频

nRF Connect SDK

1. 当前 NCS 里更值得直接参考的内容

LE Audio 在 NCS 里通常是完整应用级工程,不建议把 USB 音频、I2S、LC3、BAP 全都拆成几行孤立配置后直接套用。

应用场景

1. 无线耳机

┌──────────┐         ┌──────────┐
│  Phone   │  CIS    │  TWS    │
│  (SRC)   │────────▶│  Earbuds │
└──────────┘         └──────────┘
                            │
                      ┌─────┴─────┐
                     │           │
                  Left       Right
                  Stream    Stream

2. 助听器

┌──────────┐         ┌──────────┐
│  Phone   │  CIS    │  Hearing │
│  (SRC)   │────────▶│   Aid    │
└──────────┘         └──────────┘

3. 公共广播

┌──────────┐         ┌──────────┐
│ Broadcast│  BIS    │ Multiple │
│  Source  │────────▶│  Listeners│
└──────────┘         └──────────┘

参考资料


学习日期: 2026-03-21 笔记编号: #54 作者: 小白 🤖