单麦唤醒算法示例

功能说明

演示如何使用 ACOMP 唤醒算法组件实现语音唤醒功能,包括音频数据采集、回采处理、唤醒检测和结果输出。

本示例展示了单麦克风的唤醒算法应用,支持关键词识别、超时处理以及算法回声消除音频输出等完整的唤醒流程。

用户可以通过语音唤醒算法,算法检测到唤醒词或命令词后,会输出识别结果。

硬件连接

  • 音频输入: 单麦克风输入(MIC1),用ADC采样,采样率 16kHz

  • 音频输出: DAC单声道播放输出,同时软回采参考信号,采样率 16kHz

  • 调试串口: CP核的UART0,PA3引脚,波特率 921600,用于日志输出

  • 调试串口: AP核的UART2,PA4引脚,波特率 921600,用于日志输出

示例内容

  1. 初始化 ACOMP 唤醒算法引擎

  2. 配置音频输入输出设备(单通道录音: mic0 + ref0(软回采) + 单声道播放)

  3. 创建音频数据流通道(M2R 和 R2M)

  4. 采集音频数据并融合麦克风和回采信号(录音第二路作为参考回采)

  5. 将融合后的音频数据送入唤醒算法引擎

  6. 处理唤醒结果(关键词、角度、超时等事件)

  7. 输出回声消除后的音频数据流

编译

重要提示:在编译前,请先确认您使用的开发板型号。SDK 目前支持以下开发板:

  • arcs_evb - ARCS EVB 评估板

  • arcs_mini - ARCS Mini 开发板

根据您的开发板型号,选择对应的编译命令:

在示例目录下执行编译:

# 使用 arcs_evb 开发板
./build.sh -C -DBOARD=arcs_evb

# 或使用 arcs_mini 开发板
./build.sh -C -DBOARD=arcs_mini

Note

如果在 SDK 根目录执行,需要指定示例路径:

# 使用 arcs_evb 开发板
./build.sh -C -S samples/<示例路径> -DBOARD=arcs_evb

# 或使用 arcs_mini 开发板
./build.sh -C -S samples/<示例路径> -DBOARD=arcs_mini

Note

确保已安装对应的工具链。

烧录

# 烧录 AP 核固件(Boot Core)
# 烧录地址:0x30000000
cskburn -s /dev/ttyACM0 -b 3000000 -C arcs 0x00 ./res/ap.bin    

# 烧录算法资源到 0x30200000 和 0x304d0000
cskburn -s /dev/ttyACM0 -b 3000000 -C arcs 0x200000 ./res/algo/algo.bin
cskburn -s /dev/ttyACM0 -b 3000000 -C arcs 0x4d0000 ./res/algo/wrap.json

# 烧录 CP 核固件
# 烧录地址:0x30050000
cskburn -s /dev/ttyACM0 -b 3000000 -C arcs 0x500000 ./build/arcs.bin

预期输出

CP日志

初始化阶段:

********Arcs SDK 0.0.22 @ v0.0.23.temp.docs-41-g718db8e869c5********
Running on hart-id: 1
I/elog            [341:39:56.907 1 elog_async] EasyLogger V2.2.99 is initialize success.

I/lisa_audio_record [341:39:56.907 1 audio_dispatch] LISA Audio Record initialized

I/lisa_audio_play [341:39:56.907 1 audio_dispatch] LISA Audio Play initialized

I/main            [341:39:56.908 1 main] CP=======! Hard ID: 1

I/main            [341:39:56.909 1 main] ic_message_init done!

I/acomp_ipc       [341:39:58.910 1 rpc_client] [0]acomp remote dev index 1 name acomp.wakeup

I/acomp_wakeup    [341:39:58.910 1 main] acomp wakeup init enter

I/acomp_wakeup    [341:39:58.910 1 main] acomp wakeup dev index 1,name:acomp.wakeup

I/acomp_wakeup    [341:39:58.912 1 main] acomp wakeup init exit

I/acomp_wakeup    [341:39:58.912 1 main] acomp wakeup prepare enter

I/acomp_wakeup    [341:39:58.913 1 main] acomp wakeup prepare exit

I/wakeup          [341:39:58.913 1 main] acomp_wakeup_prepare ret:0

I/acomp_wakeup    [341:39:58.913 1 main] acomp wakeup start enter

I/acomp_wakeup    [341:39:59.482 1 main] acomp wakeup start exit

I/wakeup          [341:39:59.482 1 main] acomp_wakeup_start ret:0

I/acomp_wakeup    [341:39:59.483 1 main] acomp wakeup set algo mode enter, mode=0

I/acomp_wakeup    [341:39:59.484 1 main] acomp wakeup set algo mode exit

I/wakeup          [341:39:59.484 1 main] acomp_wakeup_set_algo_mode ret:0

INF:[acomp_stream_channel_create]Acomp stream channel 0x2883f754,0x20015b50,'stream.tocloud' created successfully ,vring phy addr=0x2880d660, num_descs=8, buffer_size=25600, direction=R2M, role=Master

I/acomp_wakeup    [341:39:59.488 1 main] acomp_wakeup_stream_ch_enable chn(stream.tocloud) index(1),desc(0x2880cd08)

INF:[acomp_stream_channel_create]Acomp stream channel 0x28844834,0x20015c38,'stream.mix2ch' created successfully ,vring phy addr=0x288437a0, num_descs=4, buffer_size=1024, direction=M2R, role=Master

I/acomp_wakeup    [341:39:59.490 1 main] acomp_wakeup_stream_ch_enable chn(stream.mix2ch) index(0),desc(0x2880ccf8)

I/wakeup_in       [341:39:59.491 1 main] Audio 设备获取成功

I/wakeup_in       [341:39:59.491 1 main] 统一回调注册成功

I/lisa_audio_record [341:39:59.491 1 main] Record configured: rate=16000, gain=30/0 dB

I/lisa_audio_play [341:39:59.492 1 main] Play configured: rate=16000, gain=0/-18 dB, buffers=12×256

I/lisa_audio_record [341:39:59.492 1 main] Record started

I/wakeup_in       [341:39:59.492 1 main] 录音已启动

I/lisa_audio_play [341:39:59.492 1 main] Play started

I/wakeup_in       [341:39:59.493 1 main] 播音已启动

I/wakeup_in       [341:39:59.493 1 main] 相位补偿已设置

唤醒检测成功:

  • 用户可以用语音来唤醒算法,算法检测到唤醒词或命令词后,会输出识别结果

  • 唤醒词:小聆小聆

  • 命令词:无

注意: 算法需要被唤醒后,才可以识别命令词

I/wakeup          [341:40:05.778 1 rpc_client] WAKE(1): KEY=0(xiao3 ling2 xiao3 ling2), NCM=157, len:371

唤醒超时:

算法需要被唤醒后,才可以识别命令词,如长时间没有识别到命令词,会触发超时事件,用户需重新唤醒

I/wakeup          [341:40:44.745 1 rpc_client] wakeup timeout

AP日志

********Arcs SDK 0.0.22 @ v0.0.23.temp.docs-41-g718db8e869c5********
Running on hart-id: 0
I/elog            [00:00:00.039 0 elog_async] EasyLogger V2.2.99 is initialize success.

AP Hard ID: 0
boot cp from address: 0x30500000
luna_version:0x3000200
I/wakeup          [00:00:00.055 0 main] sys_acomp_wakeup_init

I/components      [00:00:00.056 0 main] Reserved driver ID: 1

I/components      [00:00:00.056 0 main] acomp register driver index:1,name:acomp.wakeup

INF:[acomp_context_init 257]components context init success
I/wakeup          [00:00:02.056 0 rpc_async_serv] wakeup_create enter

I/wakeup          [00:00:02.056 0 rpc_async_serv] wakeup_create exit

I/wakeup          [00:00:02.058 0 acomp_workqueue] wakeup_prepare enter

I/wakeup          [00:00:02.058 0 acomp_workqueue] wakeup_prepare exit

I/wakeup          [00:00:02.059 0 acomp_workqueue] wakeup_start enter

I/wakeup_algo     [00:00:02.059 0 acomp_workqueue] cae_esr_mlp Resource, addr=0x30200000, size=1431296

I/wakeup_algo     [00:00:02.059 0 acomp_workqueue] Wrap json, addr=0x304d0000, size=763

JSON content (763 chars):
{"wrap":{"dev":"4002","trans":0,"param":[{"key":"wakesoffset","val":160},{"key":"wakeeoffset","val":-160}]},"cae":{"dist":30,"bits":16,"chns":{"mic":1,"ref":1},"wkmask":3,"param":[{"key":"mode","val":"0"},{"key":"textdep","val":1}]},"esr":{"param":[{"key":"prewstat","val":5},{"key":"prewthr","val":10000},{"key":"mode","val":1},{"key":"model","val":0},{"key":"1305","val":1},{"key":"1106","val":0},{"key":"2119","val":0},{"key":"1124","val":1},{"key":"1180","val":2},{"key":"1126","val":63},{"key":"1125","val":10},{"key":"1105","val":2},{"key":"1118","val":8},{"key":"1119","val":40},{"key":"1130","val":5},{"key":"1131","val":8},{"key":"1136","val":1}],"switchparam":[{"key":"2118","val_main":[500],"val_cmd":[0]},{"key":"1107","val_main":[2],"val_cmd":[2]}]}}
I/wakeup_algo     [00:00:02.069 0 acomp_workqueue]
ai_mem_t memap: json: 0x304d0000, psram: 0x0, size: 0, res: 0x30200000, size: 1431296

ai_get_info ret=0, len=790
info content (790 chars):
{"wrap":{"version":"600x.aiwrap.v1000.1.0.1 beta arcs_sp (Aug 12 2025 18:47:23)","inst_size":8608,"share_size":2560,"samples":{"in":256,"out":1280}},"cae":{"version":"arcs.cae.v1000.0.0.1 beta (Aug 12 2025 18:04:12)","memory":{"inst_ram_size":29184,"temp_ram_size":51200,"psram_size":0},"feature":{"mic_num":1,"ref_num":1,"local_out_channels":1,"aec_out_channels":0,"asr_out_channels":0,"nbit":16,"nsample":16000,"frame_num":5,"frame_size":256,"support_pre_wk_info":0,"support_wk_info":0}},"esr":{"version":"mini-esr-ARCS Tag5.1.2.1.2.5_26,Aug 12 2025,10:17:16","memory":{"share_mem_size":58848,"inst_mem_size":60288},"feature":{"input_pcm_channel":1,"input_pcm_bit":16,"input_pcm_frame_count":8,"input_pcm_sample_rate":16000,"max_main_word_count":1,"max_asr_word_count":1,"alog_type":26}}}
I/wakeup_algo     [00:00:02.206 0 acomp_workqueue] wrap_size: 8608

I/wakeup_algo     [00:00:02.206 0 acomp_workqueue] cae_psram: 0

I/wakeup_algo     [00:00:02.206 0 acomp_workqueue] inst_ram_size: 29184

I/wakeup_algo     [00:00:02.206 0 acomp_workqueue] temp_ram_size: 51200

I/wakeup_algo     [00:00:02.206 0 acomp_workqueue] esr_share_mem: 58848

I/wakeup_algo     [00:00:02.206 0 acomp_workqueue] esr_psram: 60288

I/wakeup_algo     [00:00:02.207 0 acomp_workqueue] Memory requirements - psram_size: 68928, share_size: 88064

I/wakeup_algo     [00:00:02.207 0 acomp_workqueue] algo_psram_buffer allocated at 0x28024e80, size=68928

Resource: mlp.bin,addr: 0x28035bc0, size: 1223424, compress: 0
Resource: main.bin,addr: 0x281606c0, size: 912, compress: 0
Resource: cmds.bin,addr: 0x28160a60, size: 6048, compress: 0
Resource: cae_ffw0_100.json,addr: 0x28162200, size: 141, compress: 0
Resource: cae_aes.bin,addr: 0x281622a0, size: 200000, compress: 0
I/wakeup_algo     [00:00:02.625 0 acomp_workqueue] ai_create success(used: psram=0x10d00, share=0x157e0)

I/wakeup_algo     [00:00:02.626 0 acomp_workqueue] esrstag: 1

I/wakeup_algo     [00:00:02.626 0 acomp_workqueue] algo_set_mode(0)

I/wakeup_algo     [00:00:02.627 0 acomp_workqueue] MODE=0

I/wakeup          [00:00:02.627 0 acomp_workqueue] wakeup_start exit

I/wakeup          [00:00:02.628 0 acomp_workqueue] wakeup_control enter

I/wakeup          [00:00:02.629 0 acomp_workqueue] wakeup_control: WAKEUP_IPC_CONTROL_SUBCMD_ALGO_MODE_SET

I/wakeup          [00:00:02.629 0 acomp_workqueue] Set algo mode: 0

I/wakeup_algo     [00:00:02.629 0 acomp_workqueue] Algo mode set to: WAKEUP

INF:[acomp_stream_channel_create]Acomp stream channel 0x28004a20,0x20051d34,'stream.tocloud' created successfully ,vring phy addr=0x2880d660, num_descs=8, buffer_size=25600, direction=R2M, role=Remote

INF:[acomp_stream_channel_create]Acomp stream channel 0x28004a50,0x20051da0,'stream.mix2ch' created successfully ,vring phy addr=0x288437a0, num_descs=4, buffer_size=1024, direction=M2R, role=Remote

I/wakeup_algo     [00:00:04.307 0 acomp_workqueue] counter_avg=119.07(M/S), cnt_part=190512871

I/wakeup_algo     [00:00:05.907 0 acomp_workqueue] counter_avg=125.59(M/S), cnt_part=200938084

I/wakeup_algo     [00:00:07.507 0 acomp_workqueue] counter_avg=125.71(M/S), cnt_part=201138800

I/wakeup_algo     [00:00:09.107 0 acomp_workqueue] counter_avg=125.68(M/S), cnt_part=201081353


核心 API

API

说明

acomp_wakeup_init()

初始化唤醒算法组件

acomp_wakeup_prepare()

准备唤醒算法引擎

acomp_wakeup_start()

启动唤醒算法引擎

acomp_wakeup_set_algo_mode()

设置算法模式(唤醒模式)

acomp_wakeup_add_callback()

注册唤醒事件回调函数

acomp_wakeup_stream_ch_enable()

使能音频数据流通道

acomp_wakeup_stream_tx_buffer_alloc()

分配发送缓冲区(M2R)

acomp_wakeup_stream_tx_buffer_submit()

提交发送缓冲区

acomp_wakeup_stream_rx_buffer_get()

获取接收缓冲区(R2M)

acomp_wakeup_stream_rx_buffer_release()

释放接收缓冲区

lisa_audio_register_callback()

注册音频设备回调

lisa_audio_record_config()

配置录音参数

lisa_audio_play_config()

配置播放参数

lisa_audio_record_start()

启动录音

lisa_audio_play_start()

启动播放

关键代码

唤醒算法事件回调

static void wakeup_event_handler(uint32_t event, void *event_data, uint32_t event_data_len, void *priv)
{
    if (event & WAKEUP_CB_EVENT_ENGINE_RLT) {
        /* 唤醒结果 */
        LISA_LOGI(TAG, "wakeup result:%d,%s", event_data_len, (char *)event_data);
    } else if (event & WAKEUP_CB_EVENT_ENGINE_TIMEOUT) {
        /* 唤醒超时 */
        LISA_LOGI(TAG, "wakeup timeout");
    } else if (event & WAKEUP_CB_EVENT_ENGINE_ANGLE) {
        /* 角度信息 */
        for (int i = 0; i < event_data_len/sizeof(short); i++) {
            short angle = *((short*)event_data + i);
            LISA_LOGD(TAG, "wakeup angle[%d]: %d", i, angle);
        }
    }
}

static void wakeup_stream_handler(uint32_t event, void *event_data, uint32_t event_data_len, void *priv)
{
    
    if (event & WAKEUP_CB_EVENT_STREAM_UPDATE) {
        /* 算法音频输出事件 */
#ifdef WAKEUP_AUDIO_OUT_STREAM_KICK_AUTO
        if (rx_sem != NULL) {
            xSemaphoreGive(rx_sem);
        }
#endif
    }
}

获取麦克风和回采的音频

/* 音频回调函数 - 收集麦克风和回采数据 */
static void unified_audio_callback(const lisa_audio_event_t *event, void *user_data)
{
    wakeup_in_msg_t *msg = psram_malloc(sizeof(wakeup_in_msg_t));
    msg->mic = (mic_in_t*)event->record_buffer;
    msg->ref = (ref_in_t*)(event->echo_buffer ? event->echo_buffer : zero_echo);
    msg->sample_cnt = event->record_samples;
    
    /* 提交到工作队列处理 */
    workqueue_submit(wakeup_in_wq, wakeup_in_wq_handler, msg);
}

麦克风和回采的音频融合后送入算法

static void wakeup_in_wq_handler(void *para)
{
    uint8_t *buffer;
    uint32_t buf_size;
    uint16_t desc_idx;

    wakeup_in_msg_t *msg = (wakeup_in_msg_t*)para;

    LISA_LOGD(LOG_TAG, "wakeup_in_wq_handler, sample_cnt: %u", msg->sample_cnt);

    buffer = acomp_wakeup_stream_tx_buffer_alloc(WAKEUP_AUDIO_MIX2CH_STREAM_CH_INDEX, &buf_size, &desc_idx);

    if(buffer && buf_size > 0){
        stream_data_fusion((acomp_wakeup_audio_in_t*)buffer, msg->mic, msg->ref, msg->sample_cnt);
        acomp_wakeup_stream_tx_buffer_submit(WAKEUP_AUDIO_MIX2CH_STREAM_CH_INDEX, buffer, buf_size, desc_idx);
    }

    psram_free(msg);
}

用户对算法输出的音频进行处理

用户可以获取算法输出的音频数据,进行处理,如发送算法输出的回声消除的音频到云端进行大模型意图识别

static void wakeup_out_task(void *pvParameters)
{
    uint8_t *buffer;
    uint32_t len;
    uint16_t desc_idx;
    int ret;

    while (1) {
#ifdef WAKEUP_AUDIO_OUT_STREAM_KICK_AUTO
        /* 自动触发模式:等待来自回调的信号量 */
        xSemaphoreTake(rx_sem, pdMS_TO_TICKS(50));
#else
        /* 手动触发模式:等待超时时间 */
        vTaskDelay(pdMS_TO_TICKS(5));
#endif
        /* 从 R2M 流中获取数据 */
        while (1) {
            buffer = acomp_wakeup_stream_rx_buffer_get(WAKEUP_AUDIO_OUT_STREAM_CH_INDEX, &len, &desc_idx);
            if (buffer != NULL && len > 0) {
                LISA_LOGD(TAG, "acomp_wakeup_stream_rx_buffer_get ok");

                // 2. 处理接收到的数据
                // 数据格式为 acomp_wakeup_audio_out_t 数组,5通道交织
                // {mic0, mic1, ref0, out1, out2} * N 个采样点
                // 其中 out1 为回声消除后的音频,可用于云端识别

                // process_output_audio(buffer, len);


                /* 释放缓冲区 */
                ret = acomp_wakeup_stream_rx_buffer_release(WAKEUP_AUDIO_OUT_STREAM_CH_INDEX, desc_idx, len, buffer);
                if (ret != ACOMP_ERR_OK) {
                    LISA_LOGW(TAG, "Failed to release buffer: %d", ret);
                }
            } else {
                /* 没有更多数据,跳出内层循环 */
                break;
            }
        }
    }
}

算法模式说明

单麦克风模式 (CONFIG_ACOMP_WAKEUP_ALGORITHM_TYPE_DUAL_MIC=n)

  • 输入通道: 1 个麦克风 + 1 个回采参考

  • 数据格式: 2 通道交织(mic1, ref1)

  • 特点: 支持波束成形和角度检测,唤醒性能更好

音频参数配置

#define SAMPLE_RATE         LISA_AUDIO_RATE_16K    /* 采样率 16kHz */
#define SAMPLE_BITS         LISA_AUDIO_BIT_16      /* 采样位宽 16bit */
#define RECORD_CHANNELS     LISA_AUDIO_CH_STEREO   /* 录音双声道 */
#define PLAY_CHANNELS       LISA_AUDIO_CH_LEFT     /* 播放单声道 */
#define BUFFER_SAMPLES      ACOMP_WAKEUP_AUDIO_INPUT_SAMPLE_CNT    /* 缓冲区采样点数 */

增益配置

#define RECORD_ANALOG_GAIN     30      /* 录音模拟增益 16dB */
#define RECORD_DIGITAL_GAIN    0       /* 录音数字增益 8dB */
#define PLAY_ANALOG_GAIN       0       /* 播放模拟增益 6dB */
#define PLAY_DIGITAL_GAIN      -18     /* 播放数字增益 -18dB */

算法资源配置

prj.conf 中配置算法资源地址和大小:

CONFIG_ACOMP_WAKEUP_ALGORITHM_TYPE_DUAL_MIC=n
CONFIG_ACOMP_WAKEUP_RES_CAE_ESR_MLP_ADDRESS=0x30200000
CONFIG_ACOMP_WAKEUP_RES_CAE_ESR_MLP_LENGTH=1431296
CONFIG_ACOMP_WAKEUP_RES_AI_WRAP_ADDRESS=0x304D0000
CONFIG_ACOMP_WAKEUP_RES_AI_WRAP_LENGTH=763

堆内存配置

CONFIG_HEAP_SIZE=0x10000           /* 内部 SRAM  64KB */
CONFIG_PSRAM_HEAP_SIZE=0x700000    /* PSRAM  7MB */

数据流说明

M2R 流(Master to Remote)

  • 通道索引: 0

  • 通道名称: “stream.mi x 1ch”

  • 方向: 应用核(CP) → 算法核(AP)

  • 数据: 融合后的麦克风和回采数据(输入给算法)

  • 缓冲区大小: 一次1024 字节(256 采样点 × 2 通道 × 2 字节)

R2M 流(Remote to Master)

  • 通道索引: 1

  • 通道名称: “stream.tocloud”

  • 方向: 算法核(AP) → 应用核(CP)

  • 数据: 算法处理后的音频数据(3 通道)(mic1, ref, 回声消除后的音频(可用于意图识别), 算法后端输入音频)

  • 缓冲区大小: 一次输出 4帧 或6帧

算法内存占用

单麦算法内存占用如下(粗略统计), 详细内存分布见memap.h文件

MCU核心

内存类型

大小

备注

AP

FLASH

1674KB

2个算法资源 + ap固件大小 + cp固件大小

AP

PSRAM

1550KB

算法实例用的PSRAM + 2个算法资源拷贝到PSRAM上 + 动态内存

AP

SRAM

8KB

IPC共享内存

AP

SRAM

62KB

AP的SRAM

AP

SRAM

290KB

算法实例大小

AP

SRAM

28KB

LUNA的*(.sharedmem.*)

AP

LUNASRAM

64KB

LUNA专用的SRAM大小