雷鸟vad集成,已用实时录音验证通过 2124/1
authorluozheng <1732036298@qq.com>
Fri, 22 Aug 2025 02:19:56 +0000 (10:19 +0800)
committerluozheng <1732036298@qq.com>
Fri, 22 Aug 2025 02:19:56 +0000 (10:19 +0800)
16 files changed:
code/sdk/application/brandy/brandy_standard/CMakeLists.txt
code/sdk/application/brandy/brandy_standard/service_init.c
code/sdk/build/config/target_config/brandy/config.py
code/sdk/middleware/services/media/foundation/audio_capturer/interfaces/kits/audio_capturer_lite/CMakeLists.txt
code/sdk/zhihui/CMakeLists.txt
code/sdk/zhihui/leiniao/CMakeLists.txt [new file with mode: 0644]
code/sdk/zhihui/leiniao/imsl_ln/CMakeLists.txt [new file with mode: 0644]
code/sdk/zhihui/leiniao/imsl_ln/REAL_VAD_TEST_GUIDE.md [new file with mode: 0644]
code/sdk/zhihui/leiniao/imsl_ln/imsl_system_adapter.c [new file with mode: 0644]
code/sdk/zhihui/leiniao/imsl_ln/include/imsl_kws.h [new file with mode: 0644]
code/sdk/zhihui/leiniao/imsl_ln/include/imsl_vad.h [new file with mode: 0644]
code/sdk/zhihui/leiniao/imsl_ln/lib/libimslkws.a [new file with mode: 0644]
code/sdk/zhihui/leiniao/imsl_ln/lib/libimslnn.a [new file with mode: 0644]
code/sdk/zhihui/leiniao/imsl_ln/lib/libimslvad.a [new file with mode: 0644]
code/sdk/zhihui/leiniao/imsl_ln/vad_at_cmd.c [new file with mode: 0644]
code/sdk/zhihui/leiniao/imsl_ln/vad_at_cmd.h [new file with mode: 0644]

index 92a47bd..1680093 100644 (file)
@@ -44,6 +44,7 @@ set(PRIVATE_HEADER
     ${ROOT_DIR}/zhihui/4g/cat1Service/include
     ${ROOT_DIR}/zhihui/4g/cat1Service/Service
     ${ROOT_DIR}/zhihui/4gTest
+    ${ROOT_DIR}/zhihui/leiniao/imsl_ln/
 )
 
 set(PUBLIC_HEADER
index d6f1d7d..db162c7 100644 (file)
@@ -96,6 +96,9 @@
 #ifdef SUPPORT_AT_BP\r
 #include "bp_at_cmd.h"\r
 #endif\r
+#ifdef SUPPORT_AT_VAD\r
+#include "vad_at_cmd.h"\r
+#endif\r
 static void register_at_service(void)\r
 {\r
 #ifdef SUPPORT_AT_GRAPHIC\r
@@ -222,6 +225,11 @@ static void register_at_service(void)
     at_cmd_table_t *bp_table = get_bp_cmd_table(&bp_cmd_count);\r
     at_cmd_add_moudle(bp_table, bp_cmd_count);\r
 #endif\r
+#ifdef SUPPORT_AT_VAD\r
+    uint32_t vad_cmd_count;\r
+    at_cmd_table_t *vad_table = get_vad_cmd_table(&vad_cmd_count);\r
+    at_cmd_add_moudle(vad_table, vad_cmd_count);\r
+#endif\r
 }\r
 \r
 #ifdef CONFIG_OTA_UPDATE_SUPPORT\r
index f2cbfba..0d37dd3 100644 (file)
@@ -421,6 +421,7 @@ target = {
                            "ppg_sensor_hx3695",
                         #   "ppgBleService",
                            "mag_sensor_mmc5603",
+                           "thunderbird_toys",#雷鸟
                           "vibrator",
                           "vibrator_service",
                         #   "-:lwip",
index e474e77..8cac94b 100644 (file)
@@ -38,6 +38,8 @@ set(PRIVATE_HEADER
     
     ${ROOTDIR}/open_source/openharmony/foundation/multimedia/audio_capturer/interfaces/kits
     ${ROOT_DIR}/application/wearable/service/xiaodu_voice
+    # IMSL VAD/KWS headers
+    ${ROOTDIR}/zhihui/leiniao/imsl_ln/include
 )
 
 set(CCDEFINES ${CCDEFINES} "_GNU_SOURCE" "_XOPEN_SOURCE=700" "UNSUPPORT_AUDIO_DECODER")
index 2ad2f14..8155e70 100644 (file)
@@ -9,6 +9,7 @@ add_subdirectory_if_exist(utilscpp)
 add_subdirectory_if_exist(drivers)
 add_subdirectory_if_exist(config)
 add_subdirectory_if_exist(UI)
+add_subdirectory_if_exist(leiniao)
 
 if("TEST_4G_SDK" IN_LIST DEFINES)
     add_subdirectory_if_exist(4gsdk)
diff --git a/code/sdk/zhihui/leiniao/CMakeLists.txt b/code/sdk/zhihui/leiniao/CMakeLists.txt
new file mode 100644 (file)
index 0000000..03ce15e
--- /dev/null
@@ -0,0 +1 @@
+add_subdirectory_if_exist(imsl_ln)
\ No newline at end of file
diff --git a/code/sdk/zhihui/leiniao/imsl_ln/CMakeLists.txt b/code/sdk/zhihui/leiniao/imsl_ln/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a860f8c
--- /dev/null
@@ -0,0 +1,68 @@
+#===============================================================================
+# @brief    imsl_ln component cmake file
+# Copyright (c) @CompanyNameMagicTag 2023-2023. All rights reserved.
+#===============================================================================
+set(COMPONENT_NAME "thunderbird_toys")
+
+# 源文件:系统适配层实现
+set(SOURCES
+    ${CMAKE_CURRENT_SOURCE_DIR}/imsl_system_adapter.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/vad_at_cmd.c
+)
+
+# 公共头文件路径(供其他模块引用)
+set(PUBLIC_HEADER
+    ${CMAKE_CURRENT_SOURCE_DIR}/include  # 包含imsl_vad.h和imsl_kws.h的目录
+)
+
+# 公共宏定义(可选)
+set(PUBLIC_DEFINES
+    # 例如:IMSL_LN_ENABLE=1
+    THUNDERBIRD_TOYS
+    SUPPORT_AT_VAD
+)
+
+# 私有头文件路径(仅当前组件内部使用)
+set(PRIVATE_HEADER
+    # 如无特殊私有头文件,可留空
+)
+
+# 私有宏定义(可选)
+set(PRIVATE_DEFINES
+    # 如无特殊私有宏,可留空
+)
+
+# 公共编译选项(可选)
+set(COMPONENT_PUBLIC_CCFLAGS
+)
+
+# 组件专属编译选项
+set(COMPONENT_CCFLAGS
+    -Wno-incompatible-function-pointer-types
+    -Wno-unused-function  # 屏蔽未使用函数的警告(根据实际情况调整)
+)
+
+# 是否启用全量链接(静态库通常需要)
+set(WHOLE_LINK
+    true
+)
+
+# 是否为主组件(通常为false,主组件一般是app)
+set(MAIN_COMPONENT
+    false
+)
+
+# 安装SDK时包含的文件(头文件和库文件)
+# install_sdk(${CMAKE_CURRENT_SOURCE_DIR}/include "*.h")  # 安装头文件
+# install_sdk(${CMAKE_CURRENT_SOURCE_DIR}/lib "*.a")      # 安装静态库(假设.a在lib目录)
+install_sdk(${CMAKE_CURRENT_SOURCE_DIR} "*")
+
+# 直接指定静态库全路径(目录下现有文件名为 libimslvad.a、libimslkws.a、libimslnn.a)
+set(LIBS
+    ${CMAKE_CURRENT_SOURCE_DIR}/lib/libimslvad.a
+    ${CMAKE_CURRENT_SOURCE_DIR}/lib/libimslkws.a
+    ${CMAKE_CURRENT_SOURCE_DIR}/lib/libimslnn.a
+)
+
+# 构建组件
+build_component()
\ No newline at end of file
diff --git a/code/sdk/zhihui/leiniao/imsl_ln/REAL_VAD_TEST_GUIDE.md b/code/sdk/zhihui/leiniao/imsl_ln/REAL_VAD_TEST_GUIDE.md
new file mode 100644 (file)
index 0000000..936e9ac
--- /dev/null
@@ -0,0 +1,273 @@
+# 🎤 真实语音数据验证 VAD 功能指南
+
+## 🎯 目标
+使用真实语音数据验证 VAD 功能,确保语音活动检测的准确性和实时性。
+
+## 📋 完整的 VAD AT 指令集
+
+### 1. 基础指令
+```
+AT^VAD_INIT                    # 初始化 VAD
+AT^VAD_STATUS                  # 查询 VAD 状态
+AT^VAD_CLEANUP                 # 清理 VAD 资源
+AT^VAD_HELP                    # 显示帮助信息
+```
+
+### 2. 配置指令
+```
+AT^VAD_CONFIG <sil_threshold> <energy_threshold> <min_speech_length> <silence_detect_length> <rule1_min_tailing_silence> <rule2_min_tailing_silence> <rule3_min_tailing_silence>
+```
+
+**参数说明:**
+- `sil_threshold`: 静音阈值 (默认: 63, 建议: 50-70)
+- `energy_threshold`: 能量阈值 (默认: 10000, 建议: 5000-20000)
+- `min_speech_length`: 最小语音长度 (默认: 240, 建议: 120-480)
+- `silence_detect_length`: 静音检测长度 (默认: 300, 建议: 200-500)
+- `rule1_min_tailing_silence`: 规则1最小尾部静音 (默认: 480, 建议: 240-960)
+- `rule2_min_tailing_silence`: 规则2最小尾部静音 (默认: 960, 建议: 480-1920)
+- `rule3_min_tailing_silence`: 规则3最小尾部静音 (默认: 960, 建议: 480-1920)
+
+### 3. 测试指令
+```
+AT^VAD_TEST_AUDIO <audio_type>  # 测试音频数据
+AT^VAD_RECORD_START             # 开始录音测试
+AT^VAD_RECORD_STOP              # 停止录音测试
+```
+
+**audio_type 参数:**
+- `0`: 静音测试
+- `1`: 语音测试
+- `2`: 混合测试(静音+语音)
+
+## 🎵 真实语音数据验证方法
+
+### 方法 1:使用真实麦克风录音
+
+#### 步骤 1:初始化 VAD
+```bash
+AT^VAD_INIT
+```
+**预期输出:**
+```
+=== VAD AT 指令测试 ===
+✓ 日志回调设置成功
+✓ VAD 初始化成功 (状态: 1)
+```
+
+#### 步骤 2:配置优化参数(可选)
+```bash
+AT^VAD_CONFIG 50 5000 120 300 480 960 960
+```
+**参数说明:**
+- 降低静音阈值:63 → 50(更容易检测到语音)
+- 降低能量阈值:10000 → 5000(对低音量语音更敏感)
+- 降低最小语音长度:240 → 120(更快响应短语音)
+
+#### 步骤 3:开始录音测试
+```bash
+AT^VAD_RECORD_START
+```
+**预期输出:**
+```
+=== VAD 录音测试开始 ===
+请对着麦克风说话,系统将实时检测语音活动
+按任意键停止录音测试
+```
+
+#### 步骤 4:进行语音测试
+1. **静音测试**:保持安静 3-5 秒
+   - 预期:VAD 状态 = 0(静音)
+   
+2. **语音测试**:清晰地说 "你好" 或 "测试"
+   - 预期:VAD 状态 = 1(语音)
+   
+3. **语音结束测试**:停止说话,保持安静
+   - 预期:VAD 状态 = 2(语音结束),然后自动变为 0
+
+#### 步骤 5:停止录音测试
+```bash
+AT^VAD_RECORD_STOP
+```
+
+### 方法 2:使用预录制的语音文件
+
+#### 步骤 1:准备测试音频文件
+- 格式:16kHz, 16bit, 单声道 PCM
+- 内容:包含静音-语音-静音的完整序列
+- 时长:建议 10-30 秒
+
+#### 步骤 2:音频数据处理
+```c
+// 读取音频文件并分帧处理
+FILE *audio_file = fopen("test_audio.pcm", "rb");
+int16_t audio_frame[160]; // 10ms @ 16kHz
+
+while (fread(audio_frame, sizeof(int16_t), 160, audio_file) == 160) {
+    // 运行 VAD
+    run_vad(audio_frame, 160);
+    
+    // 获取状态
+    int status = vad_detect();
+    printf("帧 %d: VAD状态 = %d\n", frame_count++, status);
+    
+    // 处理语音结束
+    if (status == 2) {
+        printf("检测到语音结束\n");
+        vad_clear();
+    }
+}
+```
+
+### 方法 3:实时音频流测试
+
+#### 步骤 1:配置音频采集
+```c
+// 配置音频采集参数
+struct AudioConfig audio_conf = {
+    .sample_rate = 16000,
+    .bit_depth = 16,
+    .channels = 1,
+    .frame_size = 160  // 10ms
+};
+```
+
+#### 步骤 2:实时 VAD 处理
+```c
+// 音频采集回调函数
+void audio_callback(int16_t *audio_data, int data_len) {
+    // 分帧处理(每帧 160 采样点)
+    for (int i = 0; i < data_len; i += 160) {
+        int16_t frame[160];
+        memcpy(frame, &audio_data[i], 160 * sizeof(int16_t));
+        
+        // 运行 VAD
+        run_vad(frame, 160);
+        int status = vad_detect();
+        
+        // 实时输出状态
+        if (status != prev_status) {
+            printf("VAD状态变化: %d -> %d\n", prev_status, status);
+            prev_status = status;
+            
+            if (status == 2) {
+                printf("语音结束\n");
+                vad_clear();
+            }
+        }
+    }
+}
+```
+
+## 🔍 VAD 状态验证要点
+
+### 状态 0:静音
+- **触发条件**:音频能量低,特征不明显
+- **验证方法**:保持安静,观察状态是否稳定为 0
+- **预期行为**:状态稳定,无频繁变化
+
+### 状态 1:语音
+- **触发条件**:检测到语音特征,能量超过阈值
+- **验证方法**:清晰说话,观察状态是否变为 1
+- **预期行为**:语音开始后状态变为 1,持续到语音结束
+
+### 状态 2:语音结束
+- **触发条件**:语音特征消失,静音持续一定时间
+- **验证方法**:停止说话,观察状态是否变为 2
+- **预期行为**:状态变为 2 后自动调用 `vad_clear()`,然后变为 0
+
+## 📊 性能验证指标
+
+### 响应时间
+- **目标**:< 100ms
+- **测试方法**:从说话开始到状态变为 1 的时间
+- **测量工具**:秒表或系统时钟
+
+### 准确率
+- **目标**:> 95%
+- **测试方法**:统计正确检测的语音片段数量
+- **计算公式**:正确检测数 / 总测试数 × 100%
+
+### 误检率
+- **目标**:< 5%
+- **测试方法**:统计误报的语音检测数量
+- **计算公式**:误报数 / 总测试数 × 100%
+
+## 🚀 高级测试场景
+
+### 场景 1:环境噪声测试
+- **背景**:播放环境噪声(空调、风扇、交通等)
+- **目标**:确保 VAD 不会误报环境噪声为语音
+- **方法**:在噪声环境中保持静音,观察状态
+
+### 场景 2:多人语音测试
+- **背景**:多人同时说话
+- **目标**:验证 VAD 对混合语音的处理能力
+- **方法**:2-3 人同时说话,观察状态变化
+
+### 场景 3:音量变化测试
+- **背景**:从轻声到大声的语音变化
+- **目标**:验证 VAD 对不同音量语音的适应性
+- **方法**:逐渐提高说话音量,观察检测效果
+
+## 🛠️ 故障排除
+
+### 问题 1:VAD 状态始终为 0
+**可能原因:**
+- 能量阈值过高
+- 静音阈值过低
+- 音频数据格式不正确
+
+**解决方案:**
+```bash
+AT^VAD_CONFIG 50 3000 120 300 480 960 960
+```
+
+### 问题 2:VAD 状态频繁变化
+**可能原因:**
+- 阈值设置不当
+- 环境噪声干扰
+- 音频质量差
+
+**解决方案:**
+```bash
+AT^VAD_CONFIG 60 8000 240 400 600 1200 1200
+```
+
+### 问题 3:语音结束检测延迟
+**可能原因:**
+- 尾部静音阈值过高
+- 静音检测长度过长
+
+**解决方案:**
+```bash
+AT^VAD_CONFIG 50 5000 120 200 300 600 600
+```
+
+## 🎉 成功验证标志
+
+当 VAD 功能正常工作时,你应该看到:
+
+1. **初始化成功**:`✓ VAD 初始化成功 (状态: 1)`
+2. **状态变化正常**:静音(0) → 语音(1) → 结束(2) → 静音(0)
+3. **响应及时**:语音开始后 100ms 内状态变化
+4. **误检率低**:环境噪声不会误报为语音
+5. **资源管理**:正确调用 `vad_clear()` 和 `free_vad()`
+
+## 📝 测试记录模板
+
+```
+测试时间: _____________
+测试环境: _____________
+测试人员: _____________
+
+测试结果:
+□ 静音检测正常
+□ 语音检测正常  
+□ 语音结束检测正常
+□ 响应时间 < 100ms
+□ 误检率 < 5%
+
+备注: _____________
+```
+
+按照这个指南进行测试,你就能全面验证 VAD 功能的正确性和性能了!🎯
diff --git a/code/sdk/zhihui/leiniao/imsl_ln/imsl_system_adapter.c b/code/sdk/zhihui/leiniao/imsl_ln/imsl_system_adapter.c
new file mode 100644 (file)
index 0000000..a57546a
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * @file imsl_system_adapter.c
+ * @brief IMSL 库系统适配层,提供系统内存管理接口
+ * @copyright Copyright (c) 2023-2023. All rights reserved.
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+
+/**
+ * @brief 初始化系统堆内存
+ * @note IMSL 库内部调用,这里提供空实现
+ */
+void sys_heap_init(void)
+{
+    // 空实现,IMSL 库内部调用
+}
+
+/**
+ * @brief 系统内存分配
+ * @param size 请求的内存大小
+ * @return 分配的内存指针,失败返回 NULL
+ */
+void *sys_malloc(size_t size)
+{
+    return malloc(size);
+}
+
+/**
+ * @brief 系统内存释放
+ * @param ptr 要释放的内存指针
+ */
+void sys_free(void *ptr)
+{
+    free(ptr);
+}
diff --git a/code/sdk/zhihui/leiniao/imsl_ln/include/imsl_kws.h b/code/sdk/zhihui/leiniao/imsl_ln/include/imsl_kws.h
new file mode 100644 (file)
index 0000000..43ce640
--- /dev/null
@@ -0,0 +1,140 @@
+#ifndef _IMSL_KWS_DLL\r
+#define _IMSL_KWS_DLL\r
+\r
+#ifndef _IMSL_NN_COMMON\r
+#define _IMSL_NN_COMMON\r
+#include <stdint.h>\r
+\r
+typedef int (*imsl_nn_printf_func)(const char *format, ...);\r
+\r
+struct AuthInfo\r
+{\r
+    char dmp_token[64];\r
+};\r
+#endif\r
+\r
+struct KwsConfig\r
+{\r
+    int min_activate_len;  // 最小检测多少ms的激活才算检测到关键词\r
+    int max_activate_gap;  // 关键词激活点最长持续时间   比如  100ms 时检测到关键词但是未达到最短长度,直到100ms + max_activate_gap 这个时间点使激活失效\r
+    int active_threshold;  // 激活阈值\r
+\r
+    int kws_log_level;     // kws log打印控制, 0 不打印,1 INFO,2 INFO DEBUG\r
+\r
+    struct AuthInfo auth_info;\r
+};\r
+\r
+/*\r
+初始化kws解码器,默认配置见下方的example\r
+*/\r
+int init_kws(const struct KwsConfig* conf);\r
+\r
+int auth_kws(const struct AuthInfo* auth_info);\r
+\r
+void run_kws(const int16_t* data, const int data_len);\r
+\r
+int kws_detect(void);\r
+\r
+void kws_clear(void);\r
+\r
+void free_kws(void);\r
+\r
+#endif\r
+\r
+/* example 只使用kws\r
+// 设置log函数\r
+extern imsl_nn_printf_func imsl_nn_printf;\r
+imsl_nn_printf = printf;\r
+\r
+// 初始化\r
+struct KwsConfig kws_conf;\r
+kws_conf.min_activate_len = 10;\r
+kws_conf.active_threshold = 32;\r
+kws_conf.max_activate_gap = 160;\r
+kws_conf.kws_log_level = 0;\r
+\r
+int kws_status = init_kws(&kws_conf);\r
+\r
+struct AuthInfo auth_info;\r
+strcpy(auth_info.dmp_token, dmp_token);\r
+auth_kws(&auth_info);\r
+\r
+// 在每次接收语音时调用  注意 如果是void* 或 int8* 的指针,长度 / 2\r
+// 运行结束后获取kws状态\r
+run_kws(wave, chunk_size / 2);\r
+int kws_status = kws_detect();\r
+if (kws_status == 1) {\r
+    kws_clear();\r
+}\r
+\r
+// 不使用kws或kws暂时停用时调用\r
+free_kws();\r
+\r
+*/\r
+\r
+\r
+/* example 2 配合vad使用\r
+// 初始化\r
+\r
+\r
+struct VadConfig vad_conf;\r
+vad_conf.sil_threshold = 63;\r
+vad_conf.energy_threshold = 5000;\r
+vad_conf.min_speech_length = 240;\r
+vad_conf.silence_detect_length = 300;\r
+vad_conf.rule1_min_tailing_silence = 480;\r
+vad_conf.rule2_min_tailing_silence = 960;\r
+vad_conf.rule3_min_tailing_silence = 960;\r
+vad_conf.vad_log_level = 2;\r
+\r
+int vad_status = init_vad(&vad_conf);\r
+\r
+struct KwsConfig kws_conf;\r
+kws_conf.min_activate_len = 10;\r
+kws_conf.active_threshold = 32;\r
+kws_conf.max_activate_gap = 160;\r
+kws_conf.kws_log_level = 0;\r
+\r
+int kws_status = init_kws(&kws_conf);\r
+\r
+const int chunk_size = 480;         // 这里的chunk size每次送入kws语音的点数  比如 30ms 就是 30 * 16\r
+const int chunk_cache = 9;          // 由于vad存在一定延时,需要cache一些chunk防止丢失头部语音\r
+const int cache_size = chunk_cache * chunk_size;  // chunk_cache 的值需要根据chunk_size的值变化   9*480/chunk_size\r
+int16_t wave_cache[cache_size] = {0};  // !!!!!!!!!注意我这边是int16_t\r
+int prev_vad_status = 0;              // 记录vad状态,只在vad刚检测到语音时使用cache语音\r
+\r
+\r
+// 每次接收到语音时\r
+run_vad(wave + i, chunk_size);\r
+\r
+if (vad_status == 1) {\r
+    if (prev_vad_status == 0 || prev_vad_status == 2) {\r
+        for (j = 0; j < chunk_cache; j++) {\r
+            u32 time4 = timer_get_ms();\r
+            run_kws(wave_cache + j * chunk_size, chunk_size);\r
+            u32 time5 = timer_get_ms();\r
+            printf("run kws takes %d ms\n", time5 - time4);\r
+        }\r
+    }\r
+    run_kws(wave + i, chunk_size);\r
+    int kws_status = kws_detect();\r
+    if (kws_status == 1) {\r
+        printf("!!!!!!!!!!!!!!!!!!!!!!keyword detected!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");\r
+        kws_clear();\r
+    }\r
+} else if (vad_status == 2) {\r
+    vad_clear();\r
+}\r
+\r
+prev_vad_status = vad_status;\r
+memcpy(wave_cache, wave_cache + chunk_size, sizeof(int16_t) * (cache_size - chunk_size));\r
+memcpy(wave_cache + (cache_size - chunk_size), wave + i, sizeof(int16_t) * chunk_size);\r
+\r
+\r
+// kws暂停使用时\r
+free_kws();\r
+\r
+// vad暂停使用时\r
+free_vad();\r
+\r
+*/\r
diff --git a/code/sdk/zhihui/leiniao/imsl_ln/include/imsl_vad.h b/code/sdk/zhihui/leiniao/imsl_ln/include/imsl_vad.h
new file mode 100644 (file)
index 0000000..0fb9e20
--- /dev/null
@@ -0,0 +1,103 @@
+#ifndef _IMSL_VAD_DLL\r
+#define _IMSL_VAD_DLL\r
+#include <stdint.h>\r
+\r
+#ifndef _IMSL_NN_COMMON\r
+#define _IMSL_NN_COMMON\r
+typedef int (*imsl_nn_printf_func)(const char *format, ...);\r
+\r
+struct AuthInfo\r
+{\r
+    char dmp_token[64];\r
+};\r
+#endif\r
+\r
+// 注意这个必须要与imsl_vad.h中的定义一致\r
+struct VadConfig\r
+{\r
+    int vad_log_level;                        // 0 不启用  1 INFO  2 INFO、DEBUG\r
+    int sil_threshold;                        // 静音阈值   推荐值 63 (0-127)  (修改意义不大)\r
+    int energy_threshold;                     // 能量阈值   推荐值 10000\r
+    int min_speech_length;                    // 最短语音长度   240 (须小于 rule1_min_tailing_silence)\r
+    int silence_detect_length;                // vad_silence_detect 接口判定的尾部静音长度, 如果不需要vad_silence_detect接口可以忽略这个参数\r
+    int rule1_min_tailing_silence;            // 多长时间没有检测到语音重置VAD状态 (ms)        480\r
+    int rule2_min_tailing_silence;            // 0-10s最短多少静音判定语音结束 (ms)           960  \r
+    int rule3_min_tailing_silence;            // 10s以上最短多少静音判定语音结束 (ms)          960\r
+\r
+    struct AuthInfo auth_info;\r
+};\r
+\r
+/* 初始化vad\r
+详细参数设置参照 imsl_vad.h\r
+返回值 :\r
+    0  初始化失败\r
+    1  试用模式 (最长30分钟)\r
+    // 2  授权成功 (如果授权失败控制台会打印错误信息,同时转为试用模式)\r
+*/\r
+int init_vad(const struct VadConfig* conf);\r
+\r
+/* 授权vad\r
+返回值:\r
+    0  初始化失败\r
+    1  初始化成功\r
+*/\r
+int auth_vad(const struct AuthInfo* auth_info);\r
+\r
+// 运行vad函数,data为16khz  16bit的pcm音频数据  \r
+// 注意data_len是int16_t的data_len,如果传入u8*或void* 数据data_len需要 / 2\r
+void run_vad(const int16_t* data, int data_len);\r
+\r
+// 获取VAD的状态\r
+// 0 静音  1 语音   2 语音结束\r
+int vad_detect(void);\r
+\r
+// 获取尾部静音的状态\r
+// 1 尾部静音超过规定时长,0 尾部静音未超过规定时长\r
+// 注意如果要调用 vad_silence_detect,需要在vad_clear前调用\r
+int vad_silence_detect(void);\r
+\r
+// 清除尾部静音,调用这个函数后尾部静音长度会清0\r
+void vad_silence_clear(void);\r
+\r
+// 重置VAD的状态   每次在vad_detect为2时调用\r
+void vad_clear(void);\r
+\r
+// 释放VAD内存,不使用VAD或暂停使用VAD时调用\r
+void free_vad(void);\r
+\r
+#endif\r
+\r
+/*example :\r
+    #include "imsl_vad.h"\r
+\r
+    // 设置log函数\r
+    extern imsl_nn_printf_func imsl_nn_printf;\r
+    imsl_nn_printf = printf;\r
+\r
+    // 启用VAD时初始化\r
+    struct VadConfig conf;\r
+    conf.sil_threshold = 63;\r
+    conf.energy_threshold = 10000;\r
+    conf.min_speech_length = 240;\r
+    conf.silence_detect_length = 300;\r
+    conf.rule1_min_tailing_silence = 480;\r
+    conf.rule2_min_tailing_silence = 960;\r
+    conf.rule3_min_tailing_silence = 960;\r
+    conf.vad_log_level = 2;\r
+    init_vad(&conf);\r
+\r
+    struct AuthInfo auth_info;\r
+    strcpy(auth_info.dmp_token, dmp_token);\r
+    auth_vad(&auth_info);\r
+\r
+    // 每次获得语音后调用, 语音长度最好小于100ms\r
+    // wave为u8*或void*指针时需要 / 2\r
+    run_vad(wave, wave_len / 2);\r
+    int status = vad_detect();    \r
+    if (status == 2) {\r
+        vad_clear();\r
+    }\r
+\r
+    // 不使用或暂停使用VAD时调用\r
+    free_vad();\r
+*/
\ No newline at end of file
diff --git a/code/sdk/zhihui/leiniao/imsl_ln/lib/libimslkws.a b/code/sdk/zhihui/leiniao/imsl_ln/lib/libimslkws.a
new file mode 100644 (file)
index 0000000..6c07435
Binary files /dev/null and b/code/sdk/zhihui/leiniao/imsl_ln/lib/libimslkws.a differ
diff --git a/code/sdk/zhihui/leiniao/imsl_ln/lib/libimslnn.a b/code/sdk/zhihui/leiniao/imsl_ln/lib/libimslnn.a
new file mode 100644 (file)
index 0000000..0f73723
Binary files /dev/null and b/code/sdk/zhihui/leiniao/imsl_ln/lib/libimslnn.a differ
diff --git a/code/sdk/zhihui/leiniao/imsl_ln/lib/libimslvad.a b/code/sdk/zhihui/leiniao/imsl_ln/lib/libimslvad.a
new file mode 100644 (file)
index 0000000..293b4e0
Binary files /dev/null and b/code/sdk/zhihui/leiniao/imsl_ln/lib/libimslvad.a differ
diff --git a/code/sdk/zhihui/leiniao/imsl_ln/vad_at_cmd.c b/code/sdk/zhihui/leiniao/imsl_ln/vad_at_cmd.c
new file mode 100644 (file)
index 0000000..dadac51
--- /dev/null
@@ -0,0 +1,475 @@
+#include "vad_at_cmd.h"
+#include "at_cmd_api.h"
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include "imsl_vad.h"
+#include "media_thread_adapt.h"
+
+#define ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0]))
+#define VAD_MAX_ARGC 8
+#define VAD_MAX_ARGV_LEN 64
+#define VAD_FAILED (-1)
+#define VAD_SUCCESS (0)
+
+/* Indicate that a local variable is not used */
+#define VAD_UNUSED(a) ((void)(a))
+
+// 全局 VAD 状态
+static bool g_vad_initialized = false;
+static int g_vad_status = -1;
+static struct VadConfig g_vad_config = {0};
+
+// 音频缓冲区(用于真实语音测试)
+#define VAD_AUDIO_BUFFER_SIZE 3200 // 200ms @ 16kHz
+static int16_t g_audio_buffer[VAD_AUDIO_BUFFER_SIZE];
+static int g_audio_buffer_pos = 0;
+
+/* 录音封装(由 C++ 提供,C 接口声明) */
+extern int32_t AudioCaptureSample(int32_t argc, const char *argv[]);
+
+/* 将 PCM 文件按 10ms(160点) 分帧喂给 VAD 并打印结果 */
+static int AnalyzePcmFileWithVad(const char *pcm_path)
+{
+    FILE *fp = fopen(pcm_path, "rb");
+    if (fp == NULL) {
+        printf("open pcm failed: %s\n", pcm_path);
+        return VAD_FAILED;
+    }
+
+    int16_t frame[160];
+    size_t total_frames = 0;
+    int prev = -1;
+    int count_speech = 0;
+    int count_end = 0;
+
+    while (1) {
+        size_t n = fread(frame, sizeof(int16_t), 160, fp);
+        if (n < 160) {
+            break;
+        }
+        run_vad(frame, 160);
+        int st = vad_detect();
+        if (st != prev) {
+            printf("VAD状态变化: %d -> %d 于帧%zu\n", prev, st, total_frames);
+            prev = st;
+        }
+        if (st == 1) {
+            count_speech++;
+        } else if (st == 2) {
+            count_end++;
+            vad_clear();
+        }
+        total_frames++;
+    }
+    fclose(fp);
+    printf("总帧数:%zu, 语音帧:%d, 结束帧:%d\n", total_frames, count_speech, count_end);
+    return VAD_SUCCESS;
+}
+
+/* ---------------- 实时录音并同步喂给 VAD(基于文件尾随) ---------------- */
+static volatile bool g_vad_live_running = false;
+static MediaThreadIdHandle g_vad_tail_thread = NULL;
+static char g_vad_live_path[256] = "/user/vad_live.pcm";
+static int g_vad_prev_status = -1;
+
+static void *VadTailThread(void *arg)
+{
+    printf("VAD TailThread\n");
+    (void)arg;
+    FILE *fp = NULL;
+    int16_t frame[160];
+    size_t filled = 0;
+
+    /* 等待文件可读 */
+    while (g_vad_live_running && fp == NULL) {
+        fp = fopen(g_vad_live_path, "rb");
+        if (fp == NULL) {
+            usleep(50 * 1000);
+        }
+    }
+    if (!g_vad_live_running) {
+        if (fp) fclose(fp);
+        return NULL;
+    }
+
+    /* 从头开始读,持续尾随追加的数据 */
+    while (g_vad_live_running) {
+        int16_t sample;
+        size_t n = fread(&sample, sizeof(int16_t), 1, fp);
+        if (n == 1) {
+            frame[filled++] = sample;
+            if (filled == 160) {
+                run_vad(frame, 160);
+                int st = vad_detect();
+                if (st != g_vad_prev_status) {
+                    printf("[VAD-LIVE] 状态变化: %d -> %d\n", g_vad_prev_status, st);
+                    g_vad_prev_status = st;
+                    if (st == 2) {
+                        printf("[VAD-LIVE] 检测到语音结束,vad_clear()\n");
+                        vad_clear();
+                    }
+                }
+                filled = 0;
+            }
+        } else {
+            /* 到达当前 EOF,短暂等待数据追加 */
+            clearerr(fp);
+            usleep(10 * 1000);
+        }
+    }
+    if (fp) fclose(fp);
+    return NULL;
+}
+
+static void *StartRecordThread(void *arg)
+{
+    printf("VAD StartRecordThread\n");
+    (void)arg;
+    const char *startArgs[6] = {"01", "pcm", "16000", "1", "16", g_vad_live_path};
+    (void)AudioCaptureSample(6, startArgs);
+    return NULL;
+}
+
+static void EnsureParentDir(const char *file_path)
+{
+    if (file_path == NULL) {
+        return;
+    }
+    const char *slash = strrchr(file_path, '/');
+    if (slash == NULL || slash == file_path) {
+        return; // no parent or root
+    }
+    char dir[256];
+    size_t len = (size_t)(slash - file_path);
+    if (len >= sizeof(dir)) {
+        return;
+    }
+    memcpy(dir, file_path, len);
+    dir[len] = '\0';
+    struct stat st;
+    if (stat(dir, &st) == 0) {
+        return; // exists
+    }
+    (void)mkdir(dir, 0777);
+}
+
+static int ConvertCmdToParam(uint8_t *para, uint32_t paraLen, uint8_t argv[VAD_MAX_ARGC][VAD_MAX_ARGV_LEN], uint32_t *argc)
+{
+    uint32_t pos = 0;
+    *argc = 0;
+
+    for (uint32_t i = 0; i < paraLen; i++)
+    {
+        if ((para[i] == ' ' || para[i] == '\n') && pos != 0)
+        {
+            argv[*argc][pos] = '\0';
+            *argc += 1;
+            pos = 0;
+            if (*argc >= VAD_MAX_ARGC)
+            {
+                return VAD_FAILED;
+            }
+        }
+        else
+        {
+            argv[*argc][pos] = para[i];
+            pos++;
+            if (pos >= VAD_MAX_ARGV_LEN)
+            {
+                return VAD_FAILED;
+            }
+        }
+    }
+    if (pos != 0)
+    {
+        *argc += 1;
+    }
+
+    return VAD_SUCCESS;
+}
+
+// AT^VAD_INIT - 初始化 VAD
+static uint32_t vad_init(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    VAD_UNUSED(strCommonCmd);
+    uint8_t argv[VAD_MAX_ARGC][VAD_MAX_ARGV_LEN] = {0};
+    uint32_t argc = 0;
+
+    int ret = ConvertCmdToParam(para, paraLen, argv, &argc);
+    if (ret != VAD_SUCCESS)
+    {
+        printf("VADTEST ATCMD Convert Command Failed\n");
+        return ERRCODE_FAIL;
+    }
+
+    if (g_vad_initialized)
+    {
+        printf("VAD already initialized\n");
+        return ERRCODE_SUCC;
+    }
+
+    printf("=== VAD AT 指令测试 ===\n");
+
+    // 设置日志回调
+    extern imsl_nn_printf_func imsl_nn_printf;
+    imsl_nn_printf = printf;
+    printf("✓ 日志回调设置成功\n");
+
+    // 初始化 VAD 配置
+    g_vad_config.sil_threshold = 63;
+    g_vad_config.energy_threshold = 10000;
+    g_vad_config.min_speech_length = 240;
+    g_vad_config.silence_detect_length = 300;
+    g_vad_config.rule1_min_tailing_silence = 480;
+    g_vad_config.rule2_min_tailing_silence = 960;
+    g_vad_config.rule3_min_tailing_silence = 960;
+    g_vad_config.vad_log_level = 2;
+
+    int init_result = init_vad(&g_vad_config);
+    if (init_result == 0)
+    {
+        printf("❌ VAD 初始化失败\n");
+        return ERRCODE_FAIL;
+    }
+
+    printf("✓ VAD 初始化成功 (状态: %d)\n", init_result);
+    g_vad_initialized = true;
+    g_vad_status = 0;
+
+    return ERRCODE_SUCC;
+}
+
+// AT^VAD_CONFIG - 配置 VAD 参数
+static uint32_t vad_config(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    VAD_UNUSED(strCommonCmd);
+    uint8_t argv[VAD_MAX_ARGC][VAD_MAX_ARGV_LEN] = {0};
+    uint32_t argc = 0;
+
+    int ret = ConvertCmdToParam(para, paraLen, argv, &argc);
+    if (ret != VAD_SUCCESS)
+    {
+        printf("VADTEST ATCMD Convert Command Failed\n");
+        return ERRCODE_FAIL;
+    }
+
+    if (!g_vad_initialized)
+    {
+        printf("VAD not initialized, call AT^VAD_INIT first\n");
+        return ERRCODE_FAIL;
+    }
+
+    if (argc != 7)
+    {
+        printf("Usage: AT^VAD_CONFIG <sil_threshold> <energy_threshold> <min_speech_length> <silence_detect_length> <rule1_min_tailing_silence> <rule2_min_tailing_silence> <rule3_min_tailing_silence>\n");
+        return ERRCODE_FAIL;
+    }
+
+    // 解析参数
+    g_vad_config.sil_threshold = atoi((char *)argv[0]);
+    g_vad_config.energy_threshold = atoi((char *)argv[1]);
+    g_vad_config.min_speech_length = atoi((char *)argv[2]);
+    g_vad_config.silence_detect_length = atoi((char *)argv[3]);
+    g_vad_config.rule1_min_tailing_silence = atoi((char *)argv[4]);
+    g_vad_config.rule2_min_tailing_silence = atoi((char *)argv[5]);
+    g_vad_config.rule3_min_tailing_silence = atoi((char *)argv[6]);
+
+    printf("✓ VAD 配置更新成功\n");
+    printf("  sil_threshold: %d\n", g_vad_config.sil_threshold);
+    printf("  energy_threshold: %d\n", g_vad_config.energy_threshold);
+    printf("  min_speech_length: %d\n", g_vad_config.min_speech_length);
+
+    return ERRCODE_SUCC;
+}
+
+// AT^VAD_STATUS - 查询 VAD 状态
+static uint32_t vad_status(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    VAD_UNUSED(para);
+    VAD_UNUSED(paraLen);
+    VAD_UNUSED(strCommonCmd);
+
+    if (!g_vad_initialized)
+    {
+        printf("VAD not initialized, call AT^VAD_INIT first\n");
+        return ERRCODE_FAIL;
+    }
+
+    printf("=== VAD 状态信息 ===\n");
+    printf("初始化状态: %s\n", g_vad_initialized ? "已初始化" : "未初始化");
+    printf("当前状态: %d (0=静音, 1=语音, 2=结束)\n", g_vad_status);
+    printf("配置参数:\n");
+    printf("  sil_threshold: %d\n", g_vad_config.sil_threshold);
+    printf("  energy_threshold: %d\n", g_vad_config.energy_threshold);
+    printf("  min_speech_length: %d\n", g_vad_config.min_speech_length);
+
+    return ERRCODE_SUCC;
+}
+
+// AT^VAD_ANALYZE_PCM <pcm_path>
+static uint32_t vad_analyze_pcm(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    VAD_UNUSED(strCommonCmd);
+    if (!g_vad_initialized) {
+        printf("VAD not initialized, call AT^VAD_INIT first\n");
+        return ERRCODE_FAIL;
+    }
+    uint8_t argv[VAD_MAX_ARGC][VAD_MAX_ARGV_LEN] = {0};
+    uint32_t argc = 0;
+    if (ConvertCmdToParam(para, paraLen, argv, &argc) != VAD_SUCCESS || argc != 1) {
+        printf("Usage: AT^VAD_ANALYZE_PCM <pcm_path>\n");
+        return ERRCODE_FAIL;
+    }
+    const char *pcm_path = (const char*)argv[0];
+    printf("VAD 分析现有 PCM: %s\n", pcm_path);
+    int ret = AnalyzePcmFileWithVad(pcm_path);
+    return (ret == VAD_SUCCESS) ? ERRCODE_SUCC : ERRCODE_FAIL;
+}
+
+// AT^VAD_RECORD_START - 开始录音测试
+static uint32_t vad_record_start(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    VAD_UNUSED(strCommonCmd);
+    if (!g_vad_initialized) {
+        printf("VAD not initialized, call AT^VAD_INIT first\n");
+        return ERRCODE_FAIL;
+    }
+
+    if (g_vad_live_running) {
+        printf("VAD live already running\n");
+        return ERRCODE_SUCC;
+    }
+
+    /* 可选自定义输出路径 */
+    const char *out_path = (const char*)g_vad_live_path;
+    uint8_t argvbuf[VAD_MAX_ARGC][VAD_MAX_ARGV_LEN] = {0};
+    uint32_t argc = 0;
+    if (para != NULL && paraLen > 0 && ConvertCmdToParam(para, paraLen, argvbuf, &argc) == VAD_SUCCESS && argc >= 1) {
+        strncpy(g_vad_live_path, (const char*)argvbuf[0], sizeof(g_vad_live_path) - 1);
+        g_vad_live_path[sizeof(g_vad_live_path) - 1] = '\0';
+        out_path = g_vad_live_path;
+    }
+
+    printf("=== VAD 实时录音开始 ===\n");
+    printf("PCM 输出: %s\n", out_path);
+
+    /* 1) 确保目录存在并异步启动系统录音保存为 PCM(避免在 AT 线程上堆栈溢出) */
+    EnsureParentDir(out_path);
+    strncpy(g_vad_live_path, out_path, sizeof(g_vad_live_path) - 1);
+    g_vad_live_path[sizeof(g_vad_live_path) - 1] = '\0';
+    StartRecordThread(NULL);
+
+    /* 2) 启动尾随线程实时喂给 VAD */
+    g_vad_prev_status = -1;
+    g_vad_live_running = true;
+    {
+        MediaThreadattr attr = { "VADTail", 0x1000, THREAD_SCHED_INVALID, 0, true };
+        g_vad_tail_thread = MediaThreadCreate(VadTailThread, NULL, &attr);
+        if (g_vad_tail_thread == NULL) {
+            printf("create tail thread failed\n");
+            g_vad_live_running = false;
+            return ERRCODE_FAIL;
+        }
+    }
+    return ERRCODE_SUCC;
+}
+
+// AT^VAD_RECORD_STOP - 停止录音测试
+static uint32_t vad_record_stop(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    VAD_UNUSED(para);
+    VAD_UNUSED(paraLen);
+    VAD_UNUSED(strCommonCmd);
+
+    printf("=== 停止 VAD 实时录音 ===\n");
+
+    /* 1) 停止尾随线程 */
+    if (g_vad_live_running) {
+        g_vad_live_running = false;
+        if (g_vad_tail_thread != NULL) {
+            MediaThreadJoin(&g_vad_tail_thread);
+            g_vad_tail_thread = NULL;
+        }
+    }
+
+    /* 2) 停止系统录音 */
+    {
+        const char *stopArg = "stop";
+        (void)AudioCaptureSample(1, &stopArg);
+    }
+
+    printf("VAD 实时录音已停止\n");
+    return ERRCODE_SUCC;
+}
+
+// AT^VAD_CLEANUP - 清理 VAD 资源
+static uint32_t vad_cleanup(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    VAD_UNUSED(para);
+    VAD_UNUSED(paraLen);
+    VAD_UNUSED(strCommonCmd);
+
+    if (!g_vad_initialized)
+    {
+        printf("VAD not initialized\n");
+        return ERRCODE_SUCC;
+    }
+
+    free_vad();
+    printf("✓ VAD 资源释放成功\n");
+    g_vad_initialized = false;
+    g_vad_status = -1;
+
+    return ERRCODE_SUCC;
+}
+
+// AT^VAD_HELP - 显示帮助信息
+static uint32_t vad_help(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    VAD_UNUSED(para);
+    VAD_UNUSED(paraLen);
+    VAD_UNUSED(strCommonCmd);
+
+    printf("=== VAD AT 指令帮助 ===\n");
+    printf("AT^VAD_INIT         - 初始化 VAD\n");
+    printf("AT^VAD_CONFIG       - 配置 VAD 参数\n");
+    printf("AT^VAD_STATUS       - 查询 VAD 状态\n");
+    printf("AT^VAD_ANALYZE_PCM <in.pcm>   - 分析已有PCM\n");
+    printf("AT^VAD_RECORD_START - 开始录音测试\n");
+    printf("AT^VAD_RECORD_STOP  - 停止录音测试\n");
+    printf("AT^VAD_CLEANUP      - 清理 VAD 资源\n");
+    printf("AT^VAD_HELP         - 显示此帮助\n");
+    printf("\n=== 使用示例 ===\n");
+    printf("AT^VAD_INIT                    # 初始化\n");
+    printf("AT^VAD_CONFIG 50 5000 120 300 480 960 960  # 配置参数\n");
+    printf("AT^VAD_TEST_AUDIO 1            # 测试语音\n");
+    printf("AT^VAD_STATUS                  # 查看状态\n");
+    printf("AT^VAD_CLEANUP                 # 清理资源\n");
+    printf("=====================\n");
+
+    return ERRCODE_SUCC;
+}
+
+static const at_cmd_table_t g_vad_cmd[] = {
+    {(char *)"AT^VAD_INIT", vad_init, (char *)"初始化 VAD"},
+    {(char *)"AT^VAD_CONFIG", vad_config, (char *)"配置 VAD 参数"},
+    {(char *)"AT^VAD_STATUS", vad_status, (char *)"查询 VAD 状态"},
+    {(char *)"AT^VAD_ANALYZE_PCM", vad_analyze_pcm, (char *)"分析现有PCM"},
+    {(char *)"AT^VAD_RECORD_START", vad_record_start, (char *)"开始录音测试"},
+    {(char *)"AT^VAD_RECORD_STOP", vad_record_stop, (char *)"停止录音测试"},
+    {(char *)"AT^VAD_CLEANUP", vad_cleanup, (char *)"清理 VAD 资源"},
+    {(char *)"AT^VAD_HELP", vad_help, (char *)"显示帮助信息"},
+};
+
+at_cmd_table_t *get_vad_cmd_table(uint32_t *cmd_count)
+{
+    *cmd_count = ARRAY_COUNT(g_vad_cmd);
+    return (at_cmd_table_t *)g_vad_cmd;
+}
diff --git a/code/sdk/zhihui/leiniao/imsl_ln/vad_at_cmd.h b/code/sdk/zhihui/leiniao/imsl_ln/vad_at_cmd.h
new file mode 100644 (file)
index 0000000..d95097f
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef VAD_AT_CMD_H
+#define VAD_AT_CMD_H
+
+#include <stdint.h>
+#include <stdio.h>
+#include "errcode.h"
+#include "soc_osal.h"
+#include "at_cmd_api.h"
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+at_cmd_table_t *get_vad_cmd_table(uint32_t *cmd_count);
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+#endif