第07章 音频处理器整合
约 1499 字大约 5 分钟
2026-03-23
1. 需求分析
麦克风 -> ES8311 -> [SR -> Opus编码器] -> 小智后台
扬声器 -> ES8311 <- [Opus解码器] <- 小智后台2. 代码实现
2.1 创建音频处理器对象
audio_processor.h
#ifndef __AUDIO_PROCESSOR_H__
#define __AUDIO_PROCESSOR_H__
#include "int_es8311.h"
#include "audio_sr.h"
#include "audio_encoder.h"
#include "audio_decoder.h"
// 音频处理器对象类型别名
typedef struct audio_processor_struct audio_processor_t;
/**
* @brief 创建音频处理器对象
*
* @return audio_processor_t* 音频处理器对象指针
* */
audio_processor_t *audio_processor_create(void);
#endif /* __AUDIO_PROCESSOR_H__ */audio_processor.c
#include "audio_processor.h"
// 定义音频处理器对象的结构体
struct audio_processor_struct
{
// SR对象
audio_sr_t *audio_sr;
// opus编码器对象
audio_encoder_t *audio_encoder;
// opus解码器对象
audio_decoder_t *audio_decoder;
// 编码器输入缓冲区
RingbufHandle_t encoder_input_buffer;
// 编码器输出缓冲区
RingbufHandle_t encoder_output_buffer;
// 解码器输入缓冲区
RingbufHandle_t decoder_input_buffer;
// 解码器输出缓冲区
RingbufHandle_t decoder_output_buffer;
};
/**
* @brief 创建音频处理器对象
*
* @return audio_processor_t* 音频处理器对象指针
* */
audio_processor_t *audio_processor_create(void)
{
// 1. 为处理器对象分配内存
audio_processor_t *audio_processor = heap_caps_malloc(sizeof(audio_processor_t), MALLOC_CAP_SPIRAM);
// 2. 创建对象
// 2.1 创建 SR 对象
audio_processor->audio_sr = audio_sr_create();
// 2.2 创建 opus编码器对象
audio_processor->audio_encoder = audio_encoder_create();
// 2.3 创建 opus解码器对象
audio_processor->audio_decoder = audio_decoder_create();
// 3. 创建缓冲区
// 3.1 创建编码器输入缓冲区
audio_processor->encoder_input_buffer = xRingbufferCreateWithCaps(64 * 1024, RINGBUF_TYPE_BYTEBUF, MALLOC_CAP_SPIRAM);
// 3.2 创建编码器输出缓冲区
audio_processor->encoder_output_buffer = xRingbufferCreateWithCaps(64 * 1024, RINGBUF_TYPE_NOSPLIT, MALLOC_CAP_SPIRAM);
// 3.3 创建解码器输入缓冲区
audio_processor->decoder_input_buffer = xRingbufferCreateWithCaps(64 * 1024, RINGBUF_TYPE_NOSPLIT, MALLOC_CAP_SPIRAM);
// 3.4 创建解码器输出缓冲区
audio_processor->decoder_output_buffer = xRingbufferCreateWithCaps(64 * 1024, RINGBUF_TYPE_NOSPLIT, MALLOC_CAP_SPIRAM);
// 4. 返回处理器对象
return audio_processor;
}2.2 启动音频处理
audio_processor.h
添加如下代码:
/**
* @brief 启动音频处理器
*
* @param audio_processor 音频处理器对象指针
*/
void audio_processor_start(audio_processor_t *audio_processor);
/**
* @brief 给SR注册VAD回调函数
*
* @param audio_processor 音频处理器对象指针
* @param vad_cb VAD回调函数指针
*/
void audio_processor_register_vad_callback(audio_processor_t *audio_processor, void (*vad_cb)(vad_state_t));
/**
* @brief 给SR注册唤醒回调函数
*
* @param audio_processor 音频处理器对象指针
* @param wakeup_cb 唤醒回调函数指针
*/
void audio_processor_register_wakeup_callback(audio_processor_t *audio_processor, void (*wakeup_cb)(void));
/**
* @brief 关闭SR唤醒
*
* @param audio_processor 音频处理器对象指针
*/
void audio_processor_stop_wakeup(audio_processor_t *audio_processor);audio_processor.c
添加如下代码:
// 静态函数:播放任务
void audio_processor_play_task_func(void *arg)
{
// 获取参数
audio_processor_t *audio_processor = (audio_processor_t *)arg;
size_t len = 0;
void *data = NULL;
while (1)
{
// 解码器的输出缓冲区获取数据
data = xRingbufferReceive(audio_processor->decoder_output_buffer, &len, portMAX_DELAY);
// 将读取到的数据写入扬声器
int_es8311_write_speaker(data, len);
// 释放环形缓冲区资源
vRingbufferReturnItem(audio_processor->decoder_output_buffer, data);
}
}
// 代码省略 ...
/**
* @brief 启动音频处理器
*
* @param audio_processor 音频处理器对象指针
*/
void audio_processor_start(audio_processor_t *audio_processor)
{
// 1. 将缓冲区注册到对应的对象中
// 1.1 给SR对象注册输出缓冲区
audio_sr_register_ringbuf(audio_processor->audio_sr, audio_processor->encoder_input_buffer);
// 1.2 给opus编码器对象注册输入缓冲区和输出缓冲区
audio_encoder_register_input_buffer(audio_processor->audio_encoder, audio_processor->encoder_input_buffer);
audio_encoder_register_output_buffer(audio_processor->audio_encoder, audio_processor->encoder_output_buffer);
// 1.3 给opus解码器对象注册输入缓冲区和输出缓冲区
audio_decoder_register_input_ringbuf(audio_processor->audio_decoder, audio_processor->decoder_input_buffer);
audio_decoder_register_output_ringbuf(audio_processor->audio_decoder, audio_processor->decoder_output_buffer);
// 2. 启动对象
// 2.1 初始ES8311
int_es8311_init();
// 2.2 启动opus解码器
audio_decoder_start(audio_processor->audio_decoder);
// 2.3 启动opus编码器
audio_encoder_start(audio_processor->audio_encoder);
// 2.4 启动SR语音识别,里面自动会读ES8311的麦克风数据,然后进行语音识别
audio_sr_start(audio_processor->audio_sr);
// 3. 创建一个任务,将解码器的输出缓冲区中的数据交给ES8311播放
xTaskCreateWithCaps(audio_processor_play_task_func, "audio_processor_play_task", 32 * 1024, audio_processor, 5, NULL, MALLOC_CAP_SPIRAM);
}
/**
* @brief 给SR注册VAD回调函数
*
* @param audio_processor 音频处理器对象指针
* @param vad_cb VAD回调函数指针
*/
void audio_processor_register_vad_callback(audio_processor_t *audio_processor, void (*vad_cb)(vad_state_t))
{
audio_sr_register_vad_cb(audio_processor->audio_sr, vad_cb);
}
/**
* @brief 给SR注册唤醒回调函数
*
* @param audio_processor 音频处理器对象指针
* @param wakeup_cb 唤醒回调函数指针
*/
void audio_processor_register_wakeup_callback(audio_processor_t *audio_processor, void (*wakeup_cb)(void))
{
audio_sr_register_wakeup_cb(audio_processor->audio_sr, wakeup_cb);
}
/**
* @brief 关闭SR唤醒
*
* @param audio_processor 音频处理器对象指针
*/
void audio_processor_stop_wakeup(audio_processor_t *audio_processor)
{
audio_sr_stop_wakeup(audio_processor->audio_sr);
}2.3 向音频处理读取和写入opus数据
audio_processor.h
添加代码:
/**
* @brief 从音频处理器中读取到的Opus数据,用于发给小智
*
* @param audio_processor 音频处理器对象指针
* @param data 数据指针
* @param size 数据大小指针
* @return void
*/
void audio_processor_read_opus_data(audio_processor_t *audio_processor, uint8_t *data, size_t *size);
/**
* @brief 向音频处理器中写入Opus数据,用于接收小智的Opus数据
*
* @param audio_processor 音频处理器对象指针
* @param data 数据指针
* @param size 数据大小
*/
void audio_processor_write_opus_data(audio_processor_t *audio_processor, uint8_t *data, size_t size);audio_processor.c
添加代码
/**
* @brief 从音频处理器中读取到的Opus数据,用于发给小智
*
* @param audio_processor 音频处理器对象指针
* @param data 数据指针
* @param size 数据大小指针
* @return void*
*/
void audio_processor_read_opus_data(audio_processor_t *audio_processor, uint8_t *data, size_t *size)
{
// 从编码器的输出缓冲区中读取数据
void *opus_data = xRingbufferReceive(audio_processor->encoder_output_buffer, size, portMAX_DELAY);
// 将读取到的数据赋值给data指针
memcpy(data, opus_data, *size);
// 释放缓冲区
xRingbufferReturn(audio_processor->encoder_output_buffer, opus_data);
}
/**
* @brief 向音频处理器中写入Opus数据,用于接收小智的Opus数据
*
* @param audio_processor 音频处理器对象指针
* @param data 数据指针
* @param size 数据大小
*/
void audio_processor_write_opus_data(audio_processor_t *audio_processor, uint8_t *data, size_t size)
{
// 向解码器的输入缓冲区中写入数据
xRingbufferSend(audio_processor->decoder_input_buffer, data, size, portMAX_DELAY);
}2.4 测试
main.c
#include <stdio.h>
#include "esp_task.h"
#include "com_debug.h"
#include "int_es8311.h"
#include "audio_processor.h"
// 定义语音活动检测的回调函数
void app_main_vad_cb(vad_state_t vad_state)
{
if (vad_state == VAD_SPEECH)
{
MY_LOGI("检测到声音!");
}
else
{
MY_LOGI("检测到静音!");
}
}
// 定义检测到唤醒词次回调函数
void app_main_wakeup_cb(void)
{
MY_LOGI("AI小智被唤醒!");
}
void app_main(void)
{
MY_LOGI("音频处理器功能测试:");
// 创建音频处理器对象
audio_processor_t *audio_processor = audio_processor_create();
// 注册回调函数
audio_processor_register_vad_callback(audio_processor, app_main_vad_cb);
audio_processor_register_wakeup_callback(audio_processor, app_main_wakeup_cb);
// 启动音频处理器
audio_processor_start(audio_processor);
// 定义变量,保存从环形缓冲区获取到的数据长度
size_t len;
uint8_t *data = heap_caps_malloc(64 * 1024, MALLOC_CAP_SPIRAM);
while (1)
{
// 从音频处理器读取opus数据
audio_processor_read_opus_data(audio_processor, data, &len);
// 将opus数据写入音频处理器
audio_processor_write_opus_data(audio_processor, data, len);
}
}