初始提交卡包 USB 工程

纳入固件源码、屏幕组件、nand_xfer 工具和文档。忽略本机构建产物、sdkconfig,以及含本机路径的依赖锁。

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
zhengxin
2026-10-13 21:54:50 +08:00
co-authored by Cursor
commit 851e347cf5
260 changed files with 269349 additions and 0 deletions
+473
View File
@@ -0,0 +1,473 @@
#include "speech_app.h"
#include "speech_init.h"
#include "LVGL/lvgl_init.h"
#include <math.h>
static const char *TAG_LVGL = "sr";
// 音频设备句柄
esp_codec_dev_handle_t record_dev = NULL;
const char *cmd_phoneme[] =
{
"ni hao xiao qi",
"xiao qi zai jian", // 小奇,再见
"xiao qi tan ge zi ta", // 小奇,弹个吉他
"xiao qi bi ge xin", // 小奇,比个心
"xiao qi si kao yi xia zhe ge wen ti", // 小奇,思考一下这个问题
};
const uint32_t cmd_phoneme_number = sizeof(cmd_phoneme)/sizeof(cmd_phoneme[0]);
typedef struct __attribute__((packed)) {
char chunkID[4];
uint32_t chunkSize;
char format[4];
char subchunk1ID[4];
uint32_t subchunk1Size;
uint16_t audioFormat;
uint16_t numChannels;
uint32_t sampleRate;
uint32_t byteRate;
uint16_t blockAlign;
uint16_t bitsPerSample;
char subchunk2ID[4];
uint32_t subchunk2Size;
} wav_header_t;
void write_wav_header(FILE* fp, uint32_t total_data_bytes) {
wav_header_t wav = {
.chunkID = {'R','I','F','F'},
.format = {'W','A','V','E'},
.subchunk1ID = {'f','m','t',' '},
.subchunk1Size = 16,
.audioFormat = 1,
.numChannels = 2, // 你的参数是resdata
.sampleRate = 16000, // 你的参数是16kHz
.bitsPerSample = 16, // 你的参数是16-bit
.subchunk2ID = {'d','a','t','a'},
.subchunk2Size = total_data_bytes,
};
// 计算其他依赖的字段
wav.byteRate = wav.sampleRate * wav.numChannels * wav.bitsPerSample / 8;
wav.blockAlign = wav.numChannels * wav.bitsPerSample / 8;
wav.chunkSize = 36 + wav.subchunk2Size; // 文件总大小 - 8
// 写入文件头
fwrite(&wav, sizeof(wav), 1, fp);
}
int slience_count = 0;
#define GAIN 1.5f // 例如增益 1.5(约 +3.5 dB)
#define MAX_VAL 32767
#define MIN_VAL -32768
/**
* @brief 音频数据采集任务
* 从I2S接口读取音频数据并喂给音频前端处理
*
* @param arg 音频前端数据句柄
*/
void feed_Task(void *arg)
{
ESP_LOGI(TAG_LVGL, "Starting feed task");
esp_afe_sr_data_t *afe_data_ = arg;
// 获取音频前端处理的块大小和通道数
/*
int feed_chunksize = afe_handle->get_feed_chunksize(afe_data_);
int feed_nch = afe_handle->get_feed_channel_num(afe_data_);
ESP_LOGE(TAG_LVGL, "feed_chunksize: %d, feed_nch: %d", feed_chunksize, feed_nch);
*/
/*
FILE* fp = fopen("/extflash/audio1.wav", "wb"); // 或 "/extflash/audio.wav"
if (!fp) {
ESP_LOGW(TAG_MAIN, "WAV文件打开失败,无法写入");
}
uint32_t sampleRate = 16000;
uint16_t numChannels = 2;
uint16_t bitsPerSample = 16;
uint32_t totalSamples = sampleRate * 2; // 2秒总采样数(每声道)
uint32_t totalDataBytes = totalSamples * numChannels * (bitsPerSample / 8);
write_wav_header(fp, totalDataBytes);
int counter = 0;
*/
while (1)
{
size_t bytesIn = 0;
// 从I2S接口读取音频数据
//int ret=esp_codec_dev_read(record_dev, feed_buff, feed_chunksize * feed_nch * sizeof(int16_t)); // 读取音频数据(录音)
// 将左右声道数据求和取半,转为单声道数据
//for (int i = 0; i < feed_chunksize; i++) {
// int left = feed_buff[2*i];
// int right = feed_buff[2*i + 1];
// 求和并取平均,防止数值溢出
// mono_buff[i] = (left + right) >> 1;
//}
//ESP_LOGW(TAG_LVGL, "codec read error: %d", ret);
/*
if(counter < 33) // 采集100次后关闭文件(约10秒音频)
{
fwrite(feed_buff, sizeof(int16_t), feed_chunksize * feed_nch, fp);
counter++;
}
if(counter == 33) // 采集100次后关闭文件(约10秒音频)
{
fclose(fp);
counter++;
FILE* fp = fopen("/extflash/audio2.wav", "wb");
write_wav_header(fp, totalDataBytes);
ESP_LOGW(TAG_MAIN,"WAV 文件已保存:/extflash/audio1.wav\n");
ESP_LOGW(TAG_MAIN,"开始写入第二个文件:/extflash/audio2.wav\n");
}
if(counter > 33 && counter < 66)
{
fwrite(feed_buff, sizeof(int16_t), feed_chunksize * feed_nch, fp);
counter++;
}
if(counter == 66) // 采集100次后关闭文件(约10秒音频)
{
fclose(fp);
ESP_LOGW(TAG_MAIN,"第二个 WAV 文件已保存:/extflash/audio2.wav\n");
counter++;
FILE* fp = fopen("/extflash/audio3.wav", "wb");
write_wav_header(fp, totalDataBytes);
ESP_LOGW(TAG_MAIN,"开始写入第三个文件:/extflash/audio3.wav\n");
}
if(counter > 66 && counter < 99)
{
fwrite(feed_buff, sizeof(int16_t), feed_chunksize * feed_nch, fp);
counter++;
}
if(counter == 99) // 采集100次后关闭文件(约10秒音频)
{
fclose(fp);
ESP_LOGW(TAG_MAIN,"第三个 WAV 文件已保存:/extflash/audio3.wav\n");
counter++;
break; // 结束循环,停止采集
}
*/
// 将音频数据喂给音频前端处理
//ESP_LOGI("I2S RAW", "L:%d, R:%d, L:%d, R:%d,L:%d, R:%d,L:%d, R:%d", feed_buff[0], feed_buff[1], feed_buff[2], feed_buff[3], feed_buff[4], feed_buff[5], feed_buff[6], feed_buff[7]);
//int num_samples = feed_chunksize * feed_nch;
/*
long long sum_squares = 0;
for (int i = 0; i < num_samples; i++) {
sum_squares += (long long)feed_buff[i] * feed_buff[i];
}
double rms = sqrt((double)sum_squares / num_samples);
#define REFERENCE_RMS 750
double db = 20.0 * log10(rms / REFERENCE_RMS);
//ESP_LOGI("AUDIO", "RMS: %.2f, dB: %.2f", rms, db);
*/
//afe_handle->feed(afe_data_, feed_buff);
vTaskDelay(pdMS_TO_TICKS(1));
//ESP_LOGW(TAG_LVGL,"feed_task");
}
// 任务退出时释放资源
vTaskDelete(NULL);
}
extern bool is_ai_recording; // AI语音交互状态标志(外部声明)
extern bool is_ai_create_pcmfile; // AI语音交互生成pcm文件状态标志(外部声明)
extern bool is_ai_end_recording; // AI语音交互结束录音状态标志(外部声明)
void get_file_size(const char *path) {
struct stat st;
if (stat(path, &st) != 0) {
ESP_LOGW(TAG_MAIN,"stat failed for %s\n", path);
return;
}
float size_kb = st.st_size / 1024.0;
ESP_LOGI(TAG_MAIN, "File %s size: %.2f KB", path, size_kb);
}
/**
* @brief 语音检测任务
* 从音频前端获取处理后的数据并进行语音命令识别
*
* @param arg 音频前端数据句柄
*/
extern int64_t init_start_us;
int silence_count = 0;
void detect_Task(void *arg)
{
ESP_LOGI(TAG_LVGL, "Starting detect task");
int feed_chunksize=512;
int feed_nch=2;
// 分配音频数据缓冲区
int16_t *feed_buff = (int16_t *)heap_caps_malloc(feed_chunksize * feed_nch * sizeof(int16_t), MALLOC_CAP_SPIRAM);
assert(feed_buff); // 确保内存分配成功
int16_t *mono_buff = (int16_t *)malloc(feed_chunksize * sizeof(int16_t));
if (mono_buff == NULL) {
ESP_LOGE(TAG_LVGL, "Failed to allocate mono buffer");
return;
}
record_dev = ES7243E_Init();
//esp_afe_sr_data_t *afe_data_ = arg;
// 获取获取数据的块大小并分配缓冲区
//int afe_chunksize = afe_handle->get_fetch_chunksize(afe_data_);
//ESP_LOGW(TAG_LVGL, "afe_chunksize: %d", afe_chunksize);
/*
FILE* fp = fopen("/extflash/audio1res.wav", "wb");
if (!fp) {
ESP_LOGW(TAG_MAIN, "WAV文件打开失败,无法写入");
}
uint32_t sampleRate = 16000;
uint16_t numChannels = 1;
uint16_t bitsPerSample = 16;
uint32_t totalSamples = sampleRate * 3.2; // 3.2秒总采样数(每声道)
uint32_t totalDataBytes = totalSamples * numChannels * (bitsPerSample / 8);
write_wav_header(fp, totalDataBytes);
int counter = 0;
*/
ESP_LOGW(TAG_LVGL, "开始识别");
const int64_t init_elapsed_us = esp_timer_get_time() - init_start_us;
ESP_LOGI(TAG_MAIN, "语音识别初始化耗时: %" PRId64 " ms (%" PRId64 " us)",
init_elapsed_us / 1000, init_elapsed_us);
while (1)
{
//对话开启,启动PCM文件创建
if(is_ai_create_pcmfile == true){
FILE* fp = fopen("/extflash/audio_ai.pcm", "wb");
if (!fp) {
ESP_LOGW(TAG_MAIN, "PCM文件打开失败,无法写入,语音对话阶段一失败");
}
uint32_t sampleRate = 16000;
uint16_t numChannels = 2;
uint16_t bitsPerSample = 16;
uint32_t totalSamples = sampleRate * 2; // 2秒总采样数(每声道)
uint32_t totalDataBytes = 0; // PCM文件不需要头部,数据大小可以动态增长,需要最后填入
write_wav_header(fp, totalDataBytes);
is_ai_create_pcmfile = false;
ESP_LOGW(TAG_LVGL, "创建PCM基础格式成功");
is_ai_recording = true;//进入录音状态
fclose(fp);
}
int ret=esp_codec_dev_read(record_dev, feed_buff, feed_chunksize * feed_nch * sizeof(int16_t)); // 读取音频数据(录音)
//分贝检测
//音频录音
uint32_t totalDataBytes = 0;
if(is_ai_recording == true){
FILE* fp = fopen("/extflash/audio_ai.pcm", "ab");
if (!fp) {
ESP_LOGW(TAG_MAIN, "PCM文件录音失败,无法写入,语音对话阶段二失败");
}
fwrite(feed_buff, sizeof(int16_t), feed_chunksize * feed_nch, fp);
totalDataBytes += feed_chunksize * feed_nch * sizeof(int16_t);
//结束录音状态
fclose(fp);
//RMS对话结束检测函数
int num_samples = feed_chunksize * feed_nch;
long long sum_squares = 0;
for (int i = 0; i < num_samples; i++) {
sum_squares += (long long)feed_buff[i] * feed_buff[i];
}
double rms = sqrt((double)sum_squares / num_samples);
#define REFERENCE_RMS 750
double db = 20.0 * log10(rms / REFERENCE_RMS);
ESP_LOGI("AUDIO", "RMS: %.2f, dB: %.2f", rms, db);
if(rms<400){
silence_count++;
}
if(silence_count==53){
is_ai_recording = false;
is_ai_end_recording = true;
silence_count = 0;
}
}
// 录制结束,更新 PCM 文件头中的数据大小
if(is_ai_end_recording == true){
FILE* fp = fopen("/extflash/audio_ai.pcm", "ab");
if (!fp) {
ESP_LOGW(TAG_MAIN, "PCM文件录音结束失败,语音对话阶段三失败");
}
fseek(fp, 0, SEEK_SET); // 定位到文件开头
write_wav_header(fp, totalDataBytes); // 更新 WAV 头部中的数据
fclose(fp);
get_file_size("/extflash/audio_ai.pcm");
ESP_LOGW(TAG_LVGL, "PCM文件录制完成并更新头部成功");
change_task_post_action(0X000C);
is_ai_end_recording = false; //重置结束录音状态
}
// 将左右声道数据求和取半,转为单声道数据
for (int i = 0; i < feed_chunksize; i++) {
int left = feed_buff[2*i];
int right = feed_buff[2*i + 1];
// 求和并取平均,防止数值溢出
int avg = (left + right) >> 1; // 取平均
mono_buff[i] = (int)avg;
}
ESP_LOGI("I2S RAW", "L:%d, R:%d, L:%d, R:%d,L:%d, R:%d,L:%d, R:%d", feed_buff[0], feed_buff[1], feed_buff[2], feed_buff[3], feed_buff[4], feed_buff[5], feed_buff[6], feed_buff[7]);
ESP_LOGI("I2S RAW", "1:%d, 2:%d, 3:%d, 4:%d,5:%d, 6:%d,7:%d, 8:%d", mono_buff[0], mono_buff[1], mono_buff[2], mono_buff[3], mono_buff[4], mono_buff[5], mono_buff[6], mono_buff[7]);
// 从音频前端获取处理后的数据
//ESP_LOGW(TAG_LVGL, "detect_task");
//afe_fetch_result_t *res = afe_handle->fetch(afe_data_);
//int sample_count = res->data_size / sizeof(int16_t);
// 检查获取结果是否有效
//if (!res || res->ret_value == ESP_FAIL)
//{
// ESP_LOGE(TAG_LVGL, "获取音频数据失败!");
// res = afe_handle->fetch(afe_data_);
// break;
//}
/*
if(counter < 100) // 采集100次后关闭文件(约10秒音频)
{
fwrite(res->data, sizeof(int16_t), sample_count, fp);
counter++;
}
if(counter == 100) // 采集100次后关闭文件(约10秒音频)
{
fclose(fp);
counter++;
FILE* fp = fopen("/extflash/audio2res.wav", "wb");
write_wav_header(fp, totalDataBytes);
ESP_LOGW(TAG_MAIN,"WAV 文件已保存:/extflash/audio1res.wav\n");
ESP_LOGW(TAG_MAIN,"开始写入第二个文件:/extflash/audio2res.wav\n");
}
if(counter > 100 && counter < 200)
{
fwrite(res->data, sizeof(int16_t), sample_count, fp);
counter++;
}
if(counter == 200) // 采集100次后关闭文件(约10秒音频)
{
fclose(fp);
ESP_LOGW(TAG_MAIN,"第二个 WAV 文件已保存:/extflash/audio2res.wav\n");
counter++;
FILE* fp = fopen("/extflash/audio3res.wav", "wb");
write_wav_header(fp, totalDataBytes);
ESP_LOGW(TAG_MAIN,"开始写入第三个文件:/extflash/audio3res.wav\n");
}
if(counter > 200 && counter < 300)
{
fwrite(res->data, sizeof(int16_t), sample_count, fp);
counter++;
}
if(counter == 300) // 采集100次后关闭文件(约10秒音频)
{
fclose(fp);
ESP_LOGW(TAG_MAIN,"第三个 WAV 文件已保存:/extflash/audio3res.wav\n");
counter++;
break; // 结束循环,停止采集
}
// 获取并处理 VAD 状态
if (res->vad_state == 1) {
// 检测到语音活动
ESP_LOGI(TAG_LVGL, "VAD: Speech detected.");
} else if (res->vad_state == 0) {
// 当前为静音或噪声
ESP_LOGE(TAG_LVGL, "VAD: Silence.");
}
*/
/*
int sample_count = res->data_size / sizeof(int16_t);
int16_t *samples = res->data;
ESP_LOGI(TAG_LVGL, "data[0~5] = %d, %d, %d, %d, %d, %d",
samples[0], samples[1], samples[2],
samples[3], samples[4], samples[5]);
int16_t *samples2 = res->raw_data;
ESP_LOGI(TAG_LVGL, "raw_data[0~5] = %d, %d, %d, %d, %d, %d",
samples2[0], samples2[1], samples2[2],
samples2[3], samples2[4], samples2[5]);
*/
//ESP_LOGI(TAG_LVGL, "data_volume = %.2f dB, ringbuff_free_pct = %.2f%%",
// res->data_volume, res->ringbuff_free_pct * 100);*/
// 直接进行语音命令识别,跳过唤醒词检测
// 使用多网络模型进行语音命令检测
esp_mn_state_t mn_state = ESP_MN_STATE_DETECTING;
mn_state = multinet->detect(model_data, mono_buff);
// 如果仍在检测中,继续循环
if (ESP_MN_STATE_DETECTING == mn_state)
{
//ESP_LOGI(TAG_LVGL, "语音识别中...");
continue;
}
// 处理超时情况
if (ESP_MN_STATE_TIMEOUT == mn_state)
{
ESP_LOGW(TAG_LVGL, "语音识别超时");
sr_result_t result =
{
.state = mn_state,
.command_id = 0,
};
// xQueueSend(g_result_que, &result, 10);
continue;
}
// 处理识别到命令的情况
if (ESP_MN_STATE_DETECTED == mn_state)
{
// 获取识别结果
esp_mn_results_t *mn_result = multinet->get_results(model_data);
for (int i = 0; i < mn_result->num; i++)
{
int prob_permille = (int)(mn_result->prob[i] * 1000.0f);
ESP_LOGI(TAG_LVGL, "TOP %d, command_id: %d, phrase_id: %d, prob: %d.%03d",
i + 1, mn_result->command_id[i], mn_result->phrase_id[i],
prob_permille / 1000, abs(prob_permille % 1000));
}
int sr_command_id = mn_result->command_id[0];
// 在这里添加指令对应的中文打印
switch (sr_command_id)
{
case 0: // "ni hao xiao qi"
ESP_LOGE(TAG_LVGL, "识别到指令:你好小奇");
change_task_post_action(0X000B);
is_ai_create_pcmfile = true;
//ret =lv_obj_send_event(ui_Screen_Lottie, LV_EVENT_KEY, (void *)LV_KEY_ENTER);
ESP_LOGW(TAG_LVGL, "RET: %d", ret);
break;
case 1: // "huan ge feng ge"
ESP_LOGE(TAG_LVGL, "识别到指令:小奇,再见");
change_task_post_action(0X000C);
//ret =lv_obj_send_event(ui_Screen_Lottie, LV_EVENT_KEY, (void *)LV_KEY_NEXT);
ESP_LOGW(TAG_LVGL, "RET: %d", ret);
break;
case 2: // "yao tou huang nao"
ESP_LOGE(TAG_LVGL, "识别到指令:小奇,弹个吉他");
break;
case 3: // "yao tou huang nao"
ESP_LOGE(TAG_LVGL, "识别到指令:小奇,比个心");
break;
case 4: // "yao tou huang nao"
ESP_LOGE(TAG_LVGL, "识别到指令:小奇,思考一下这个问题");
break;
default:
ESP_LOGE(TAG_LVGL, "识别到未知指令:%d", sr_command_id);
break;
}
// 组织识别结果
sr_result_t result = {
.state = mn_state,
.command_id = sr_command_id,
};
}
vTaskDelay(pdMS_TO_TICKS(1));
}
if (feed_buff)
{
free(feed_buff);
feed_buff = NULL;
}
if (mono_buff)
{
free(mono_buff);
mono_buff = NULL;
}
vTaskDelete(NULL);
}
+30
View File
@@ -0,0 +1,30 @@
#ifndef _SPEECH_APP_H_
#define _SPEECH_APP_H_
#ifdef __cplusplus
extern "C" {
#endif
// #include "_stdint.h"
#include "config.h"
extern const uint32_t cmd_phoneme_number;
/* 拼音命令词表(与 multinet 命令短语一一对应) */
extern const char *cmd_phoneme[];
// 音频设备句柄
extern esp_codec_dev_handle_t record_dev;
void feed_Task(void *arg);
void detect_Task(void *arg);
extern lv_obj_t *ui_Screen_Lottie; // 专门用于播放动画的独立屏幕
#ifdef __cplusplus
}
#endif
#endif /* _SPEECH_APP_H_ */
+99
View File
@@ -0,0 +1,99 @@
#include "speech_init.h"
#include "speech_app.h"
#include "main.h"
srmodel_list_t *models = NULL;
afe_config_t *afe_config = NULL;
esp_afe_sr_iface_t *afe_handle = NULL;
esp_afe_sr_data_t *afe_data = NULL;
const esp_mn_iface_t *multinet = NULL;
model_iface_data_t *model_data = NULL;
char *mn_name = NULL;
static const char *TAG_LVGL = "sr";
esp_err_t app_sr_init(void)
{
//record_dev = ES7243E_Init();
/* 配置音频前端参数 */
// srmodel_list_t *models = esp_srmodel_init("model");
/*
models = esp_srmodel_init("model");
// afe_config_t *afe_config = afe_config_init("MM", models, AFE_TYPE_SR, AFE_MODE_LOW_COST);
afe_config = afe_config_init("MM", models, AFE_TYPE_SR, AFE_MODE_LOW_COST);
// afe_config>memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM;
afe_config->wakenet_model_name = NULL;
afe_config->aec_init = false; // 关闭回声消除(根据需求调整)
afe_config->vad_init = false; // 启用VAD(语音活动检测)
afe_config->pcm_config.total_ch_num = 2; // 总通道数 = 2
afe_config->pcm_config.mic_num = 2; // 麦克风通道数 = 2
afe_config->pcm_config.ref_num = 0; // 回放参考通道数 = 0
afe_config->afe_ringbuf_size = 20;
//afe_config->vad_mode = VAD_MODE_0;
afe_config->agc_init = false; // 关闭自动增益控制(根据需求调整)
afe_config->ns_init = false;
afe_config->afe_linear_gain = 2.0;
ESP_LOGE(TAG_LVGL, "配置前 afe_ringbuf_size = %d", afe_config->afe_ringbuf_size);
afe_handle = esp_afe_handle_from_config(afe_config);
// esp_afe_sr_data_t *afe_data = afe_handle->create_from_config(afe_config);
afe_data = afe_handle->create_from_config(afe_config);
*/
// char *mn_name = esp_srmodel_filter(models, ESP_MN_CHINESE, NULL);
srmodel_list_t *models = esp_srmodel_init("model");
if (models == NULL) {
ESP_LOGE(TAG_LVGL, "未找到 model 分区,跳过语音模型");
return ESP_OK;
}
mn_name = esp_srmodel_filter(models, ESP_MN_CHINESE, NULL);
if (NULL == mn_name)
{
ESP_LOGE(TAG_LVGL, "未找到多网络模型");
return ESP_OK;
}
multinet = esp_mn_handle_from_name(mn_name);
model_data = multinet->create(mn_name, 30000); // 设置语音识别超时时间
/* 配置语音命令 */
esp_mn_commands_clear(); // 清除现有命令
// 添加所有语音命令
for (int i = 0; i < cmd_phoneme_number; i++)
{
esp_mn_commands_add(i, (char *)cmd_phoneme[i]);
}
esp_mn_commands_update(); // 更新命令词列表
esp_mn_commands_print();
multinet->print_active_speech_commands(model_data); // 输出目前激活的命令词
afe_config_free(afe_config); /* 释放配置内存 */
feed_task_stack = heap_caps_malloc(FEED_TASK_STACK_SIZE, MALLOC_CAP_SPIRAM);
detect_task_stack = heap_caps_malloc(DETECT_TASK_STACK_SIZE, MALLOC_CAP_SPIRAM);
if (feed_task_stack == NULL || detect_task_stack == NULL)
{
ESP_LOGE(TAG_LVGL, "PSRAM 内存分配失败");
return ESP_OK;
}
//xTaskCreateStaticPinnedToCore(feed_Task, "feed", FEED_TASK_STACK_SIZE, (void *)afe_data, FEED_TASK_PRIORITY, feed_task_stack, &feed_task_tcb, 1);
xTaskCreateStaticPinnedToCore(detect_Task, "detect", DETECT_TASK_STACK_SIZE, (void *)afe_data, DETECT_TASK_PRIORITY, detect_task_stack, &detect_task_tcb, 1);
return ESP_OK;
}
+27
View File
@@ -0,0 +1,27 @@
#ifndef _SPEECH_INIT_H_
#define _SPEECH_INIT_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_err.h"
#include "config.h"
extern srmodel_list_t *models;
extern afe_config_t *afe_config;
extern esp_afe_sr_iface_t *afe_handle;
extern esp_afe_sr_data_t *afe_data;
extern const esp_mn_iface_t *multinet;
extern model_iface_data_t *model_data;
extern char *mn_name;
// /** 语音模块初始化(模型/命令词/任务等) */
// esp_err_t speech_init(void);
// /** 语音模块反初始化(释放模型/队列/任务等) */
// esp_err_t speech_deinit(void);
#ifdef __cplusplus
}
#endif
#endif /* _SPEECH_INIT_H_ */