imsl, minimp3 独立成项目 2571/1
author孙春阳 <snickersun@163.com>
Mon, 20 Oct 2025 11:12:09 +0000 (19:12 +0800)
committer孙春阳 <snickersun@163.com>
Mon, 20 Oct 2025 11:12:09 +0000 (19:12 +0800)
20 files changed:
code/sdk/application/brandy/brandy_standard/service_init.c
code/sdk/build/config/target_config/brandy/config.py
code/sdk/zhihui/CMakeLists.txt
code/sdk/zhihui/imsl/CMakeLists.txt [new file with mode: 0644]
code/sdk/zhihui/imsl/include/imsl_at_cmd.h [new file with mode: 0644]
code/sdk/zhihui/imsl/include/imsl_kws.h [moved from code/sdk/zhihui/leiniao/imsl_ln/include/imsl_kws.h with 100% similarity]
code/sdk/zhihui/imsl/include/imsl_vad.h [moved from code/sdk/zhihui/leiniao/imsl_ln/include/imsl_vad.h with 100% similarity]
code/sdk/zhihui/imsl/lib/libimslkws.a [moved from code/sdk/zhihui/leiniao/imsl_ln/lib/libimslkws.a with 100% similarity]
code/sdk/zhihui/imsl/lib/libimslnn.a [moved from code/sdk/zhihui/leiniao/imsl_ln/lib/libimslnn.a with 100% similarity]
code/sdk/zhihui/imsl/lib/libimslvad.a [moved from code/sdk/zhihui/leiniao/imsl_ln/lib/libimslvad.a with 100% similarity]
code/sdk/zhihui/imsl/src/imsl_at_cmd.c [new file with mode: 0644]
code/sdk/zhihui/imsl/src/imsl_system_adapter.c [moved from code/sdk/zhihui/leiniao/imsl_ln/imsl_system_adapter.c with 100% similarity]
code/sdk/zhihui/leiniao/imsl_ln/CMakeLists.txt
code/sdk/zhihui/leiniao/imsl_ln/leiniao_api_at.c
code/sdk/zhihui/minimp3/CMakeLists.txt [new file with mode: 0644]
code/sdk/zhihui/minimp3/include/minimp3.h [moved from code/sdk/zhihui/utils/minimp3.h with 100% similarity]
code/sdk/zhihui/minimp3/include/mp3_decoder.h [moved from code/sdk/zhihui/utils/mp3_decoder.h with 52% similarity]
code/sdk/zhihui/minimp3/src/mp3_decoder.c [new file with mode: 0644]
code/sdk/zhihui/utils/README_MP3_DECODER.md [deleted file]
code/sdk/zhihui/utils/mp3_decoder.c [deleted file]

index 9b7fedc..c09e1b9 100644 (file)
 #include "ai_V2_5_at_cmd.h"\r
 #endif\r
 \r
+#ifdef IMSL_VAD\r
+#include "imsl_at_cmd.h"\r
+#endif\r
+\r
 static void register_at_service(void)\r
 {\r
 #ifdef SUPPORT_AT_GRAPHIC\r
@@ -269,6 +273,12 @@ static void register_at_service(void)
     at_cmd_add_moudle(ai_table, ai_cmd_count);\r
 #endif\r
 \r
+#ifdef IMSL_VAD\r
+    uint32_t imsl_vad_cmd_count;\r
+    at_cmd_table_t *imsl_vad_table = get_imsl_vad_at_cmd_table(&imsl_vad_cmd_count);\r
+    at_cmd_add_moudle(imsl_vad_table, imsl_vad_cmd_count);\r
+#endif\r
+\r
 }\r
 \r
 #ifdef CONFIG_OTA_UPDATE_SUPPORT\r
index 8bad25f..879ab85 100644 (file)
@@ -1494,6 +1494,9 @@ target = {
                         "ai_stub",
                         #音频相关配置
                         "audio_manager",
+
+                        "imsl",
+                        "minimp3",
                         #   "CustomUIcomponentTool",
                           "nativeabilityfwk",
                           "custom_utils",
index b6c40a3..6a1ca3e 100644 (file)
@@ -28,3 +28,5 @@ add_subdirectory_if_exist(websocket)
 add_subdirectory_if_exist(4gTestcpp)
 add_subdirectory_if_exist(ppp)
 add_subdirectory_if_exist(ai_new)
+add_subdirectory_if_exist(imsl)
+add_subdirectory_if_exist(minimp3)
diff --git a/code/sdk/zhihui/imsl/CMakeLists.txt b/code/sdk/zhihui/imsl/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ef684dd
--- /dev/null
@@ -0,0 +1,67 @@
+#===============================================================================
+# @brief    imsl_ln component cmake file
+# Copyright (c) @CompanyNameMagicTag 2023-2023. All rights reserved.
+#===============================================================================
+set(COMPONENT_NAME "imsl")
+
+# 源文件:系统适配层实现
+set(SOURCES
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/imsl_system_adapter.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/imsl_at_cmd.c
+)
+
+# 公共头文件路径(供其他模块引用)
+set(PUBLIC_HEADER
+    ${CMAKE_CURRENT_SOURCE_DIR}/include  # 包含imsl_vad.h和imsl_kws.h的目录
+)
+
+# 公共宏定义(可选)
+set(PUBLIC_DEFINES
+    # 例如:IMSL_LN_ENABLE=1
+    IMSL_VAD=1
+)
+
+# 私有头文件路径(仅当前组件内部使用)
+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/imsl/include/imsl_at_cmd.h b/code/sdk/zhihui/imsl/include/imsl_at_cmd.h
new file mode 100644 (file)
index 0000000..95c4214
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef IMSL_AT_CMD_H
+#define IMSL_AT_CMD_H
+
+#include <stdint.h>
+#include "at_cmd_api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief 获取 IMSL VAD AT 命令表
+ * @param cmd_count 输出参数,返回命令数量
+ * @return AT 命令表指针
+ */
+at_cmd_table_t *get_imsl_vad_at_cmd_table(uint32_t *cmd_count);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* IMSL_AT_CMD_H */
+
diff --git a/code/sdk/zhihui/imsl/src/imsl_at_cmd.c b/code/sdk/zhihui/imsl/src/imsl_at_cmd.c
new file mode 100644 (file)
index 0000000..e0e3344
--- /dev/null
@@ -0,0 +1,536 @@
+#include "at_cmd_api.h"
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include "errcode.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 = NULL; /* heap allocated */
+static int g_audio_buffer_pos = 0;
+
+/* 录音封装(由 C++ 提供,C 接口声明) */
+extern int32_t AudioCaptureSample(int32_t argc, const char *argv[]);
+
+// 实时录音相关
+static volatile bool g_vad_live_running = false;
+static MediaThreadIdHandle g_vad_tail_thread = NULL;
+#define LN_VAD_PATH_CAP 256
+static char *g_vad_live_path = NULL; /* heap allocated, cap LN_VAD_PATH_CAP */
+static int g_vad_prev_status = -1;
+
+/* 工具函数:参数解析 */
+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++)
+    {
+        /* 以空格/换行/逗号/等号作为分隔符,兼容 "AT^CMD=arg1,arg2" 或 "AT^CMD arg1 arg2" */
+        if ((para[i] == ' ' || para[i] == '\n' || para[i] == ',' || para[i] == '=') && pos != 0)
+        {
+            argv[*argc][pos] = '\0';
+            *argc += 1;
+            pos = 0;
+            if (*argc >= VAD_MAX_ARGC)
+            {
+                return VAD_FAILED;
+            }
+        }
+        else
+        {
+            if (para[i] == ' ' || para[i] == '\n' || para[i] == ',' || para[i] == '=') {
+                continue; /* 连续分隔符跳过 */
+            }
+            argv[*argc][pos] = para[i];
+            pos++;
+            if (pos >= VAD_MAX_ARGV_LEN)
+            {
+                return VAD_FAILED;
+            }
+        }
+    }
+    if (pos != 0)
+    {
+        *argc += 1;
+    }
+
+    return VAD_SUCCESS;
+}
+
+/* 将 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;
+}
+
+static void EnsureVadLivePathInitialized(void)
+{
+    if (g_vad_live_path == NULL)
+    {
+        g_vad_live_path = (char *)malloc(LN_VAD_PATH_CAP);
+        if (g_vad_live_path)
+        {
+            g_vad_live_path[0] = '\0';
+            strncpy(g_vad_live_path, "/user/vad_live.pcm", LN_VAD_PATH_CAP - 1);
+            g_vad_live_path[LN_VAD_PATH_CAP - 1] = '\0';
+        }
+    }
+}
+
+// 尾随线程,持续从文件中读取数据并喂给 VAD
+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)
+    {
+        EnsureVadLivePathInitialized();
+        fp = fopen(g_vad_live_path ? g_vad_live_path : "/user/vad_live.pcm", "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;
+    EnsureVadLivePathInitialized();
+    const char *startArgs[6] = {"01", "pcm", "16000", "1", "16", g_vad_live_path ? g_vad_live_path : "/user/vad_live.pcm"};
+    (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);
+}
+
+// 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;
+    }
+
+    /* 可选自定义输出路径 */
+    EnsureVadLivePathInitialized();
+    const char *out_path = (const char *)(g_vad_live_path ? g_vad_live_path : "/user/vad_live.pcm");
+    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)
+    {
+        if (g_vad_live_path)
+        {
+            strncpy(g_vad_live_path, (const char *)argvbuf[0], LN_VAD_PATH_CAP - 1);
+            g_vad_live_path[LN_VAD_PATH_CAP - 1] = '\0';
+            out_path = g_vad_live_path;
+        }
+    }
+
+    printf("=== VAD 实时录音开始 ===\n");
+    printf("PCM 输出: %s\n", out_path);
+
+    /* 1) 确保目录存在并异步启动系统录音保存为 PCM(避免在 AT 线程上堆栈溢出) */
+    EnsureParentDir(out_path);
+    if (g_vad_live_path)
+    {
+        strncpy(g_vad_live_path, out_path, LN_VAD_PATH_CAP - 1);
+        g_vad_live_path[LN_VAD_PATH_CAP - 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) 先停止系统录音,减少文件追加,便于尾随线程退出 */
+    printf("VAD 停止系统录音\n");
+    {
+        const char *stopArg = "stop";
+        (void)AudioCaptureSample(1, &stopArg);
+    }
+
+    /* 2) 通知尾随线程退出(不阻塞等待) */
+    printf("VAD 停止尾随线程\n");
+    g_vad_live_running = false;
+    g_vad_tail_thread = NULL;
+
+    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_STATUS                               # 查看状态\n");
+    printf("AT^VAD_ANALYZE_PCM=/user/vad_live.pcm       # 分析已有PCM\n");
+    printf("AT^VAD_RECORD_START                         # 开始录音测试\n");
+    printf("AT^VAD_RECORD_STOP                          # 停止录音测试\n");
+    printf("AT^VAD_CLEANUP                              # 清理资源\n");
+    printf("=====================\n");
+
+    return ERRCODE_SUCC;
+}
+
+/* VAD AT 命令表 */
+static const at_cmd_table_t g_vad_at_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 *)"显示帮助信息"},
+};
+
+/* 导出函数:获取 VAD AT 命令表 */
+at_cmd_table_t *get_imsl_vad_at_cmd_table(uint32_t *cmd_count)
+{
+    *cmd_count = ARRAY_COUNT(g_vad_at_cmd);
+    return (at_cmd_table_t *)g_vad_at_cmd;
+}
+
index 6ce3f05..d8c4e76 100644 (file)
@@ -6,7 +6,6 @@ set(COMPONENT_NAME "thunderbird_toys")
 
 # 源文件:系统适配层实现
 set(SOURCES
-    ${CMAKE_CURRENT_SOURCE_DIR}/imsl_system_adapter.c
     ${CMAKE_CURRENT_SOURCE_DIR}/leiniao_api_at.c
     ${CMAKE_CURRENT_SOURCE_DIR}/ln_api.c
     ${CMAKE_CURRENT_SOURCE_DIR}/ln_api_token.c
@@ -25,7 +24,6 @@ set(SOURCES
 
 # 公共头文件路径(供其他模块引用)
 set(PUBLIC_HEADER
-    ${CMAKE_CURRENT_SOURCE_DIR}/include  # 包含imsl_vad.h和imsl_kws.h的目录
     ${ROOT_DIR}/zhihui/4g/cat1Service/include/
     ${ROOT_DIR}/zhihui/leiniao/ln_api_cpp/
     ${ROOT_DIR}/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/include
@@ -33,7 +31,6 @@ set(PUBLIC_HEADER
     ${ROOT_DIR}/open_source/openharmony/third_party/cJSON/
     ${ROOT_DIR}/open_source/openharmony/foundation/ace/ace_engine_lite/modules/netstack/websocket/include
     ${ROOT_DIR}/zhihui/websocket/os_ws
-
 )
 
 # 公共宏定义(可选)
@@ -78,12 +75,5 @@ set(MAIN_COMPONENT
 # 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
index fc689a7..8a78fc1 100644 (file)
@@ -9,35 +9,19 @@
 #include <sys/stat.h>
 #include <errno.h>
 #include "errcode.h"
-#include "imsl_vad.h"
-#include "media_thread_adapt.h"
 #include "ln_api.h"
 #include "ln_api_interface.h"
 #include "cJSON.h"
 
 #define ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0]))
-#define VAD_MAX_ARGC 8
-#define VAD_MAX_ARGV_LEN 64
 #define LN_MAX_ARGC 8
 #define LN_MAX_ARGV_LEN 512
-#define VAD_FAILED (-1)
-#define VAD_SUCCESS (0)
+#define LN_FAILED (-1)
+#define LN_SUCCESS (0)
 
 /* Indicate that a local variable is not used */
-#define VAD_UNUSED(a) ((void)(a))
+#define LN_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 = NULL; /* heap allocated */
-static int g_audio_buffer_pos = 0;
-
-/* 录音封装(由 C++ 提供,C 接口声明) */
-extern int32_t AudioCaptureSample(int32_t argc, const char *argv[]);
 
 /* 对话控制相关AT命令函数声明 */
 static uint32_t ln_conversation_flag_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd);
@@ -63,173 +47,6 @@ static uint32_t ln_check_dns_at(uint8_t *para, uint32_t paraLen, char *strCommon
 
 /* 内存查看辅助函数 - 使用MemoryManager接口 */
 
-/* 将 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;
-#define LN_VAD_PATH_CAP 256
-static char *g_vad_live_path = NULL; /* heap allocated, cap LN_VAD_PATH_CAP */
-static int g_vad_prev_status = -1;
-
-static void EnsureVadLivePathInitialized(void)
-{
-    if (g_vad_live_path == NULL)
-    {
-        g_vad_live_path = (char *)malloc(LN_VAD_PATH_CAP);
-        if (g_vad_live_path)
-        {
-            g_vad_live_path[0] = '\0';
-            strncpy(g_vad_live_path, "/user/vad_live.pcm", LN_VAD_PATH_CAP - 1);
-            g_vad_live_path[LN_VAD_PATH_CAP - 1] = '\0';
-        }
-    }
-}
-
-// 尾随线程,持续从文件中读取数据并喂给 VAD
-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)
-    {
-        EnsureVadLivePathInitialized();
-        fp = fopen(g_vad_live_path ? g_vad_live_path : "/user/vad_live.pcm", "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;
-    EnsureVadLivePathInitialized();
-    const char *startArgs[6] = {"01", "pcm", "16000", "1", "16", g_vad_live_path ? g_vad_live_path : "/user/vad_live.pcm"};
-    (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[LN_MAX_ARGC][LN_MAX_ARGV_LEN], uint32_t *argc)
 {
@@ -238,7 +55,7 @@ static int ConvertCmdToParam(uint8_t *para, uint32_t paraLen, uint8_t argv[LN_MA
 
     for (uint32_t i = 0; i < paraLen; i++)
     {
-        /* 以空格/换行/逗号/等号作为分隔符,兼容 “AT^CMD=arg1,arg2” 或 “AT^CMD arg1 arg2” */
+        /* 以空格/换行/逗号/等号作为分隔符,兼容 "AT^CMD=arg1,arg2" 或 "AT^CMD arg1 arg2" */
         if ((para[i] == ' ' || para[i] == '\n' || para[i] == ',' || para[i] == '=') && pos != 0)
         {
             argv[*argc][pos] = '\0';
@@ -246,7 +63,7 @@ static int ConvertCmdToParam(uint8_t *para, uint32_t paraLen, uint8_t argv[LN_MA
             pos = 0;
             if (*argc >= LN_MAX_ARGC)
             {
-                return VAD_FAILED;
+                return LN_FAILED;
             }
         }
         else
@@ -258,7 +75,7 @@ static int ConvertCmdToParam(uint8_t *para, uint32_t paraLen, uint8_t argv[LN_MA
             pos++;
             if (pos >= LN_MAX_ARGV_LEN)
             {
-                return VAD_FAILED;
+                return LN_FAILED;
             }
         }
     }
@@ -267,290 +84,16 @@ static int ConvertCmdToParam(uint8_t *para, uint32_t paraLen, uint8_t argv[LN_MA
         *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;
-    }
-
-    /* 可选自定义输出路径 */
-    EnsureVadLivePathInitialized();
-    const char *out_path = (const char *)(g_vad_live_path ? g_vad_live_path : "/user/vad_live.pcm");
-    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)
-    {
-        if (g_vad_live_path)
-        {
-            strncpy(g_vad_live_path, (const char *)argvbuf[0], LN_VAD_PATH_CAP - 1);
-            g_vad_live_path[LN_VAD_PATH_CAP - 1] = '\0';
-            out_path = g_vad_live_path;
-        }
-    }
-
-    printf("=== VAD 实时录音开始 ===\n");
-    printf("PCM 输出: %s\n", out_path);
-
-    /* 1) 确保目录存在并异步启动系统录音保存为 PCM(避免在 AT 线程上堆栈溢出) */
-    EnsureParentDir(out_path);
-    if (g_vad_live_path)
-    {
-        strncpy(g_vad_live_path, out_path, LN_VAD_PATH_CAP - 1);
-        g_vad_live_path[LN_VAD_PATH_CAP - 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) 先停止系统录音,减少文件追加,便于尾随线程退出 */
-    printf("VAD 停止系统录音\n");
-    {
-        const char *stopArg = "stop";
-        (void)AudioCaptureSample(1, &stopArg);
-    }
-
-    /* 2) 通知尾随线程退出(不阻塞等待) */
-    printf("VAD 停止尾随线程\n");
-    g_vad_live_running = false;
-    g_vad_tail_thread = NULL;
-
-    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_STATUS                               # 查看状态\n");
-    printf("AT^VAD_ANALYZE_PCM=/user/vad_live.pcm       # 分析已有PCM\n");
-    printf("AT^VAD_RECORD_START                         # 开始录音测试\n");
-    printf("AT^VAD_RECORD_STOP                          # 停止录音测试\n");
-    printf("AT^VAD_CLEANUP                              # 清理资源\n");
-    printf("=====================\n");
-
-    return ERRCODE_SUCC;
+    return LN_SUCCESS;
 }
 
 /* AT^LN_GET_TOKEN[=deviceId,deviceIccid,deviceModel,deviceVersion] */
 //获取设备Token
 static uint32_t ln_get_device_token_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para);
-    VAD_UNUSED(paraLen);
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para);
+    LN_UNUSED(paraLen);
+    LN_UNUSED(strCommonCmd);
     do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_GET_DEVICE_TOKEN, NULL, 0);
     return ERRCODE_SUCC;
 }
@@ -559,9 +102,9 @@ static uint32_t ln_get_device_token_at(uint8_t *para, uint32_t paraLen, char *st
 //清除设备Token
 static uint32_t ln_clear_device_token_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para);
-    VAD_UNUSED(paraLen);
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para);
+    LN_UNUSED(paraLen);
+    LN_UNUSED(strCommonCmd);
     uapi_ln_clear_device_token();
     return ERRCODE_SUCC;
 }
@@ -569,9 +112,9 @@ static uint32_t ln_clear_device_token_at(uint8_t *para, uint32_t paraLen, char *
 // AT^LN_SPEECH_CHAT=deviceId,deviceToken[,audioPath]
 static uint32_t ln_speech_chat_stream_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
-    VAD_UNUSED(para);
-    VAD_UNUSED(paraLen);
+    LN_UNUSED(strCommonCmd);
+    LN_UNUSED(para);
+    LN_UNUSED(paraLen);
     do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_SPEECH_CHAT_STREAM, NULL, 0);
     return ERRCODE_SUCC;
 }
@@ -579,9 +122,9 @@ static uint32_t ln_speech_chat_stream_at(uint8_t *para, uint32_t paraLen, char *
 /* 故事-选择故事:AT^LN_STORY_CHOOSE[=audioPath] */
 static uint32_t ln_story_choose_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
-    VAD_UNUSED(para);
-    VAD_UNUSED(paraLen);
+    LN_UNUSED(strCommonCmd);
+    LN_UNUSED(para);
+    LN_UNUSED(paraLen);
     do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_STORY_CHOOSE, NULL, 0);
     return ERRCODE_SUCC;
 }
@@ -589,7 +132,7 @@ static uint32_t ln_story_choose_at(uint8_t *para, uint32_t paraLen, char *strCom
 /* 故事-随机故事:AT^LN_STORY_RANDOM[=storyTheme] */
 static uint32_t ln_story_random_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
     uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0}; uint32_t argc = 0; (void)ConvertCmdToParam(para, paraLen, argv, &argc);
     ln_story_random_req_t req = {0}; if (argc >= 1) { strncpy(req.story_theme, (const char *)argv[0], sizeof(req.story_theme) - 1); }
     printf("[LN][STORY_RANDOM] theme=%s\n", req.story_theme[0] ? req.story_theme : "<random>");
@@ -600,9 +143,9 @@ static uint32_t ln_story_random_at(uint8_t *para, uint32_t paraLen, char *strCom
 /* 故事-获取退出哄睡询问:AT^LN_STORY_EXIT */
 static uint32_t ln_story_exit_inquiry_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para); 
-    VAD_UNUSED(paraLen); 
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para); 
+    LN_UNUSED(paraLen); 
+    LN_UNUSED(strCommonCmd);
     printf("[LN][STORY_EXIT_INQUIRY]\n");
     do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_STORY_EXIT_INQUIRY, NULL, 0);
     return ERRCODE_SUCC;
@@ -611,9 +154,9 @@ static uint32_t ln_story_exit_inquiry_at(uint8_t *para, uint32_t paraLen, char *
 /* 故事-确认结束哄睡模式:AT^LN_STORY_FINISH */
 static uint32_t ln_story_finish_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para); 
-    VAD_UNUSED(paraLen); 
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para); 
+    LN_UNUSED(paraLen); 
+    LN_UNUSED(strCommonCmd);
     printf("[LN][STORY_FINISH]\n");
     do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_STORY_FINISH, NULL, 0);
     return ERRCODE_SUCC;
@@ -622,9 +165,9 @@ static uint32_t ln_story_finish_at(uint8_t *para, uint32_t paraLen, char *strCom
 /* 设备-获取角色信息:AT^LN_DEV_CHARACTER */
 static uint32_t ln_device_get_character_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para); 
-    VAD_UNUSED(paraLen);
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para); 
+    LN_UNUSED(paraLen);
+    LN_UNUSED(strCommonCmd);
     printf("[LN][DEVICE_GET_CHARACTER]\n");
     do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_GET_CHARACTER, NULL, 0);
     return ERRCODE_SUCC;
@@ -633,21 +176,21 @@ static uint32_t ln_device_get_character_at(uint8_t *para, uint32_t paraLen, char
 /* 设备-获取绑定用户ID:AT^LN_DEV_USERID */
 static uint32_t ln_device_get_userid_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para); 
-    VAD_UNUSED(paraLen); 
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para); 
+    LN_UNUSED(paraLen); 
+    LN_UNUSED(strCommonCmd);
     printf("[LN][DEVICE_GET_USER_ID]\n");
     do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_GET_USER_ID, NULL, 0);
     return ERRCODE_SUCC;
 }
 static uint32_t ln_notify_bind_and_unbind_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
 
     uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
     uint32_t argc = 0;
 
-    if (ConvertCmdToParam(para, paraLen, argv, &argc) != VAD_SUCCESS) {
+    if (ConvertCmdToParam(para, paraLen, argv, &argc) != LN_SUCCESS) {
         printf("ERROR: Failed to parse parameters\n");
         return ERRCODE_FAIL;
     }
@@ -674,9 +217,9 @@ static uint32_t ln_notify_bind_and_unbind_at(uint8_t *para, uint32_t paraLen, ch
 /* OTA-获取升级任务:AT^LN_OTA_GET_TASK */
 static uint32_t ln_ota_get_task_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para); 
-    VAD_UNUSED(paraLen); 
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para); 
+    LN_UNUSED(paraLen); 
+    LN_UNUSED(strCommonCmd);
     printf("[LN][OTA_GET_TASK]\n");
     do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_OTA_GET_TASK, NULL, 0);
     
@@ -689,9 +232,9 @@ static uint32_t ln_ota_get_task_at(uint8_t *para, uint32_t paraLen, char *strCom
  */
 static uint32_t ln_ota_download_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para); 
-    VAD_UNUSED(paraLen); 
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para); 
+    LN_UNUSED(paraLen); 
+    LN_UNUSED(strCommonCmd);
     printf("[LN][OTA_DOWNLOAD]\n");
     do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_OTA_DOWNLOAD, NULL, 0);
     return ERRCODE_SUCC;
@@ -700,9 +243,9 @@ static uint32_t ln_ota_download_at(uint8_t *para, uint32_t paraLen, char *strCom
 /* AT^LN_WS_START */
 static uint32_t ln_ws_start_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para); 
-    VAD_UNUSED(paraLen); 
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para); 
+    LN_UNUSED(paraLen); 
+    LN_UNUSED(strCommonCmd);
     printf("[LN][WS_START]\n");
     int ret = ln_ws_start(NULL,2000);
     if (ret != 0)
@@ -743,7 +286,7 @@ static uint32_t ln_ws_start_with_callback_at(uint8_t *para, uint32_t paraLen, ch
 /* AT^LN_WS_STOP */
 static uint32_t ln_ws_stop_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para); VAD_UNUSED(paraLen); VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para); LN_UNUSED(paraLen); LN_UNUSED(strCommonCmd);
     printf("[LN][WS_STOP]\n");
     int ret = ln_ws_stop();
     if (ret != 0)
@@ -757,7 +300,7 @@ static uint32_t ln_ws_stop_at(uint8_t *para, uint32_t paraLen, char *strCommonCm
 /* AT^LN_WS_SEND=<text> */
 static uint32_t ln_ws_send_text_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
     uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
     uint32_t argc = 0;
     (void)ConvertCmdToParam(para, paraLen, argv, &argc);
@@ -780,9 +323,9 @@ static uint32_t ln_ws_send_text_at(uint8_t *para, uint32_t paraLen, char *strCom
 /* AT^LN_WS_FORCE_RECONNECT */
 static uint32_t ln_ws_force_reconnect_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para); 
-    VAD_UNUSED(paraLen); 
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para); 
+    LN_UNUSED(paraLen); 
+    LN_UNUSED(strCommonCmd);
     printf("[LN][WS_FORCE_RECONNECT]\n");
     int ret =0;// ln_ws_force_reconnect();
     if (ret != 0)
@@ -796,9 +339,9 @@ static uint32_t ln_ws_force_reconnect_at(uint8_t *para, uint32_t paraLen, char *
 /* AT^LN_WS_CLOSE_STATUS */
 static uint32_t ln_ws_close_status_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para); 
-    VAD_UNUSED(paraLen); 
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para); 
+    LN_UNUSED(paraLen); 
+    LN_UNUSED(strCommonCmd);
     printf("[LN][WS_CLOSE_STATUS]\n");
     // ln_ws_print_status_info();
     return ERRCODE_SUCC;
@@ -807,7 +350,7 @@ static uint32_t ln_ws_close_status_at(uint8_t *para, uint32_t paraLen, char *str
 /* AT^LN_WS_START_AUDIO_CHAT - 开始音频聊天 */
 static uint32_t ln_ws_start_audio_chat_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
     
     ln_audio_chat_command_t command_type = LN_AUDIO_CHAT_VOICE_CHAT_REQUEST; // 默认值
     
@@ -816,7 +359,7 @@ static uint32_t ln_ws_start_audio_chat_at(uint8_t *para, uint32_t paraLen, char
         uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
         uint32_t argc = 0;
         
-        if (ConvertCmdToParam(para, paraLen, argv, &argc) == VAD_SUCCESS && argc >= 1) {
+        if (ConvertCmdToParam(para, paraLen, argv, &argc) == LN_SUCCESS && argc >= 1) {
             const char *param = (const char *)argv[0];
             
             if (strcmp(param, "0") == 0 || strcmp(param, "VoiceChatRequest") == 0) {
@@ -851,9 +394,9 @@ static uint32_t ln_ws_start_audio_chat_at(uint8_t *para, uint32_t paraLen, char
 /* AT^LN_WS_END_AUDIO_CHAT - 结束音频聊天 */
 static uint32_t ln_ws_end_audio_chat_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para); 
-    VAD_UNUSED(paraLen); 
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para); 
+    LN_UNUSED(paraLen); 
+    LN_UNUSED(strCommonCmd);
     printf("[LN][WS_END_AUDIO_CHAT]\n");
     int ret = ln_ws_end_audio_chat();
     if (ret != 0)
@@ -868,7 +411,7 @@ static uint32_t ln_ws_end_audio_chat_at(uint8_t *para, uint32_t paraLen, char *s
 /* AT^LN_WS_SEND_RANDOM_STORY - 发送随机故事请求 */
 static uint32_t ln_ws_send_random_story_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
     
     const char *story_theme = "冒险"; // 默认主题
     
@@ -877,7 +420,7 @@ static uint32_t ln_ws_send_random_story_at(uint8_t *para, uint32_t paraLen, char
         uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
         uint32_t argc = 0;
         
-        if (ConvertCmdToParam(para, paraLen, argv, &argc) == VAD_SUCCESS && argc >= 1) {
+        if (ConvertCmdToParam(para, paraLen, argv, &argc) == LN_SUCCESS && argc >= 1) {
             story_theme = (const char *)argv[0];
         }
     }
@@ -897,7 +440,7 @@ static uint32_t ln_ws_send_random_story_at(uint8_t *para, uint32_t paraLen, char
 /* AT^LN_WS_SEND_VOICE_CHAT_INTERRUPT - 发送语音聊天打断请求 */
 static uint32_t ln_ws_send_voice_chat_interrupt_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
     
     const char *reason = "用户主动打断"; // 默认原因
     
@@ -906,7 +449,7 @@ static uint32_t ln_ws_send_voice_chat_interrupt_at(uint8_t *para, uint32_t paraL
         uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
         uint32_t argc = 0;
         
-        if (ConvertCmdToParam(para, paraLen, argv, &argc) == VAD_SUCCESS && argc >= 1) {
+        if (ConvertCmdToParam(para, paraLen, argv, &argc) == LN_SUCCESS && argc >= 1) {
             reason = (const char *)argv[0];
         }
     }
@@ -926,9 +469,9 @@ static uint32_t ln_ws_send_voice_chat_interrupt_at(uint8_t *para, uint32_t paraL
 /* AT^LN_WS_SLEEP */
 static uint32_t ln_ws_sleep_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para); 
-    VAD_UNUSED(paraLen); 
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para); 
+    LN_UNUSED(paraLen); 
+    LN_UNUSED(strCommonCmd);
     printf("[LN][WS_SLEEP]\n");
     int ret = ln_ws_enter_sleep();
     if (ret != 0)
@@ -942,9 +485,9 @@ static uint32_t ln_ws_sleep_at(uint8_t *para, uint32_t paraLen, char *strCommonC
 /* AT^LN_WS_WAKEUP */
 static uint32_t ln_ws_wakeup_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para); 
-    VAD_UNUSED(paraLen); 
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para); 
+    LN_UNUSED(paraLen); 
+    LN_UNUSED(strCommonCmd);
     printf("[LN][WS_WAKEUP]\n");
     int ret = ln_ws_exit_sleep();
     if (ret != 0)
@@ -958,7 +501,7 @@ static uint32_t ln_ws_wakeup_at(uint8_t *para, uint32_t paraLen, char *strCommon
 /* 音频播放: AT^LN_STORY_PLAY_NEXT */
 static uint32_t ln_audio_play_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para); VAD_UNUSED(paraLen); VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para); LN_UNUSED(paraLen); LN_UNUSED(strCommonCmd);
     printf("[LN][AUDIO_PLAY]\n");
     return ERRCODE_SUCC;
 }
@@ -966,7 +509,7 @@ static uint32_t ln_audio_play_at(uint8_t *para, uint32_t paraLen, char *strCommo
 /* 音频停止: AT^LN_STORY_STOP_AT */
 static uint32_t ln_audio_stop_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para); VAD_UNUSED(paraLen); VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para); LN_UNUSED(paraLen); LN_UNUSED(strCommonCmd);
     printf("[LN][AUDIO_STOP]\n");
     return ERRCODE_SUCC;
 }
@@ -974,7 +517,7 @@ static uint32_t ln_audio_stop_at(uint8_t *para, uint32_t paraLen, char *strCommo
 /* 播放指定文件: AT^LN_AUDIO_PLAY_FILE=file_path */
 static uint32_t ln_audio_play_file_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
     printf("[LN][AUDIO_PLAY_FILE]\n");
     if (para && paraLen > 0)
     {
@@ -992,7 +535,7 @@ static uint32_t ln_audio_play_file_at(uint8_t *para, uint32_t paraLen, char *str
 /* 暂停播放: AT^LN_AUDIO_PAUSE */
 static uint32_t ln_audio_pause_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para); VAD_UNUSED(paraLen); VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para); LN_UNUSED(paraLen); LN_UNUSED(strCommonCmd);
     printf("[LN][AUDIO_PAUSE]\n");
     // do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_AUDIO_PAUSE, NULL, 0);
     return ERRCODE_SUCC;
@@ -1001,7 +544,7 @@ static uint32_t ln_audio_pause_at(uint8_t *para, uint32_t paraLen, char *strComm
 /* 恢复播放: AT^LN_AUDIO_RESUME */
 static uint32_t ln_audio_resume_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para); VAD_UNUSED(paraLen); VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para); LN_UNUSED(paraLen); LN_UNUSED(strCommonCmd);
     printf("[LN][AUDIO_RESUME]\n");
     do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_AUDIO_RESUME, NULL, 0);
     return ERRCODE_SUCC;
@@ -1010,7 +553,7 @@ static uint32_t ln_audio_resume_at(uint8_t *para, uint32_t paraLen, char *strCom
 /* 哄睡模式开始: AT^LN_SLEEP_MODE_START[=storyTheme] */
 static uint32_t ln_sleep_mode_start_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-       VAD_UNUSED(strCommonCmd);
+       LN_UNUSED(strCommonCmd);
        const char *deviceId = uapi_ln_get_device_id();
        const char *id = (deviceId && deviceId[0]) ? deviceId : "DEVICE_LOCAL";
        const char *theme = "";
@@ -1051,9 +594,9 @@ static uint32_t ln_sleep_mode_start_at(uint8_t *para, uint32_t paraLen, char *st
 /* 哄睡模式结束: AT^LN_SLEEP_MODE_END */
 static uint32_t ln_sleep_mode_end_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-       VAD_UNUSED(para);
-       VAD_UNUSED(paraLen);
-       VAD_UNUSED(strCommonCmd);
+       LN_UNUSED(para);
+       LN_UNUSED(paraLen);
+       LN_UNUSED(strCommonCmd);
        const char *deviceId = uapi_ln_get_device_id();
        const char *id = (deviceId && deviceId[0]) ? deviceId : "DEVICE_LOCAL";
        char *json = (char*)malloc(256);
@@ -1094,13 +637,13 @@ static uint32_t ln_sleep_mode_end_at(uint8_t *para, uint32_t paraLen, char *strC
  */
 static uint32_t ln_conversation_flag_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
     
     uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
     uint32_t argc = 0;
     
     // 解析参数
-    if (ConvertCmdToParam(para, paraLen, argv, &argc) != VAD_SUCCESS) {
+    if (ConvertCmdToParam(para, paraLen, argv, &argc) != LN_SUCCESS) {
         printf("ERROR: Failed to parse parameters\n");
         return ERRCODE_FAIL;
     }
@@ -1144,13 +687,13 @@ static uint32_t ln_conversation_flag_at(uint8_t *para, uint32_t paraLen, char *s
  */
 static uint32_t ln_push_text_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
     
     uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
     uint32_t argc = 0;
     
     // 解析参数
-    if (ConvertCmdToParam(para, paraLen, argv, &argc) != VAD_SUCCESS) {
+    if (ConvertCmdToParam(para, paraLen, argv, &argc) != LN_SUCCESS) {
         printf("ERROR: Failed to parse parameters\n");
         return ERRCODE_FAIL;
     }
@@ -1184,7 +727,7 @@ static uint32_t ln_push_text_at(uint8_t *para, uint32_t paraLen, char *strCommon
 /* 恢复出厂设置: AT^LN_FACTORY */
 static uint32_t ln_factory_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para); VAD_UNUSED(paraLen); VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para); LN_UNUSED(paraLen); LN_UNUSED(strCommonCmd);
     printf("[LN][FACTORY]\n");
     do_restore_factory_setting();
     printf("[LN][FACTORY] ok\n");
@@ -1207,13 +750,13 @@ static uint32_t ln_factory_at(uint8_t *para, uint32_t paraLen, char *strCommonCm
  */
 static uint32_t ln_ui_ota_status_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
     
     uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
     uint32_t argc = 0;
     
     // 解析参数
-    if (ConvertCmdToParam(para, paraLen, argv, &argc) != VAD_SUCCESS) {
+    if (ConvertCmdToParam(para, paraLen, argv, &argc) != LN_SUCCESS) {
         printf("ERROR: Failed to parse parameters\n");
         return ERRCODE_FAIL;
     }
@@ -1260,13 +803,13 @@ static uint32_t ln_ui_ota_status_at(uint8_t *para, uint32_t paraLen, char *strCo
  */
 static uint32_t ln_ui_ota_progress_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
     
     uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
     uint32_t argc = 0;
     
     // 解析参数
-    if (ConvertCmdToParam(para, paraLen, argv, &argc) != VAD_SUCCESS) {
+    if (ConvertCmdToParam(para, paraLen, argv, &argc) != LN_SUCCESS) {
         printf("ERROR: Failed to parse parameters\n");
         return ERRCODE_FAIL;
     }
@@ -1306,7 +849,7 @@ static uint32_t ln_ui_ota_progress_at(uint8_t *para, uint32_t paraLen, char *str
 // AT^LN_SCHEDULE_REMINDER=开会提醒,您有一个会议即将开始
 static uint32_t ln_schedule_reminder_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
     const char *defaultName = "日程提醒";
     const char *defaultContent = "您有一个提醒";
     const char *name = defaultName;
@@ -1416,9 +959,9 @@ static uint32_t ln_schedule_reminder_cfg_at(uint8_t *para, uint32_t paraLen, cha
  */
 static uint32_t ln_memory_info_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para);
-    VAD_UNUSED(paraLen);
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para);
+    LN_UNUSED(paraLen);
+    LN_UNUSED(strCommonCmd);
     
     printf("\r\n=== System Memory Information ===\r\n");
     
@@ -1485,7 +1028,7 @@ static uint32_t ln_folder_size_at(uint8_t *para, uint32_t paraLen, char *strComm
 /* AT^LN_CHARACTER_CFG[=characterName,eyesShape,hairColor,eyesColor,hairStyle,screenImageAsEmoji] */
 static uint32_t ln_character_config_change_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
     const char *deviceId = uapi_ln_get_device_id();
     const char *id = (deviceId && deviceId[0]) ? deviceId : "DEVICE_LOCAL";
 
@@ -1553,9 +1096,9 @@ static uint32_t ln_character_config_change_at(uint8_t *para, uint32_t paraLen, c
 /* AT^LN_GET_DEVICE_ID */
 static uint32_t ln_get_device_id_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para);
-    VAD_UNUSED(paraLen);
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para);
+    LN_UNUSED(paraLen);
+    LN_UNUSED(strCommonCmd);
     printf("[LN][GET_DEVICE_ID]\n");
     const char *deviceId = uapi_ln_get_device_id();
     printf("deviceId: %s\n", deviceId);
@@ -1564,9 +1107,9 @@ static uint32_t ln_get_device_id_at(uint8_t *para, uint32_t paraLen, char *strCo
 /* AT^LN_GET_DEVICE_ICCID */
 static uint32_t ln_get_device_iccid_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para);
-    VAD_UNUSED(paraLen);
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para);
+    LN_UNUSED(paraLen);
+    LN_UNUSED(strCommonCmd);
     printf("[LN][GET_DEVICE_ICCID]\n");
     const char *deviceIccid = uapi_ln_get_device_iccid();
     printf("deviceIccid: %s\n", deviceIccid);
@@ -1575,9 +1118,9 @@ static uint32_t ln_get_device_iccid_at(uint8_t *para, uint32_t paraLen, char *st
 /* AT^LN_GET_DEVICE_MODEL */
 static uint32_t ln_get_device_model_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para);
-    VAD_UNUSED(paraLen);
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para);
+    LN_UNUSED(paraLen);
+    LN_UNUSED(strCommonCmd);
     printf("[LN][GET_DEVICE_MODEL]\n");
     const char *deviceModel = uapi_ln_get_device_model();
     printf("deviceModel: %s\n", deviceModel);
@@ -1586,9 +1129,9 @@ static uint32_t ln_get_device_model_at(uint8_t *para, uint32_t paraLen, char *st
 /* AT^LN_GET_DEVICE_VERSION */
 static uint32_t ln_get_device_version_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para);
-    VAD_UNUSED(paraLen);
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para);
+    LN_UNUSED(paraLen);
+    LN_UNUSED(strCommonCmd);
     printf("[LN][GET_DEVICE_VERSION]\n");
     const char *deviceVersion = uapi_ln_get_device_version();
     printf("deviceVersion: %s\n", deviceVersion);
@@ -1598,7 +1141,7 @@ static uint32_t ln_get_device_version_at(uint8_t *para, uint32_t paraLen, char *
 /* AT^LN_SET_FPS - 设置帧率 */
 static uint32_t ln_set_fps_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
     
     if (para == NULL || paraLen == 0) {
         printf("[LN][SET_FPS] Error: No parameter provided\n");
@@ -1637,9 +1180,9 @@ static uint32_t ln_set_fps_at(uint8_t *para, uint32_t paraLen, char *strCommonCm
 /* AT^LN_GET_FPS - 获取当前帧率 */
 static uint32_t ln_get_fps_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para);
-    VAD_UNUSED(paraLen);
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para);
+    LN_UNUSED(paraLen);
+    LN_UNUSED(strCommonCmd);
     
     printf("[LN][GET_FPS] Getting current FPS...\n");
     
@@ -1652,9 +1195,9 @@ static uint32_t ln_get_fps_at(uint8_t *para, uint32_t paraLen, char *strCommonCm
 /* AT^LN_DOWNLOAD_EMOJI */
 static uint32_t ln_download_emoji_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para);
-    VAD_UNUSED(paraLen);
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para);
+    LN_UNUSED(paraLen);
+    LN_UNUSED(strCommonCmd);
     printf("[LN][DOWNLOAD_EMOJI]\n");
     char *json = (char*)malloc(1024);
     if (json == NULL) {
@@ -1703,9 +1246,9 @@ static uint32_t ln_download_emoji_at(uint8_t *para, uint32_t paraLen, char *strC
 /* AT^LN_GET_CHAT_STAMINA - 获取对话体力与流量信息 */
 static uint32_t ln_get_chat_stamina_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para);
-    VAD_UNUSED(paraLen);
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para);
+    LN_UNUSED(paraLen);
+    LN_UNUSED(strCommonCmd);
     const char *id = uapi_ln_get_device_id();
     const char *token = uapi_ln_get_device_token();
     if (!id || !id[0] || !token || !token[0]) {
@@ -1729,7 +1272,7 @@ static uint32_t ln_get_chat_stamina_at(uint8_t *para, uint32_t paraLen, char *st
  */
 static uint32_t ln_chat_stamina_demo_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
     int chatLeft = 0;
     int flowLeft = 0;
     const char *chatDesc = NULL;
@@ -1779,13 +1322,13 @@ static uint32_t ln_chat_stamina_demo_at(uint8_t *para, uint32_t paraLen, char *s
  */
 static uint32_t ln_switch_expression_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
     
     uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
     uint32_t argc = 0;
     
     // 解析参数
-    if (ConvertCmdToParam(para, paraLen, argv, &argc) != VAD_SUCCESS) {
+    if (ConvertCmdToParam(para, paraLen, argv, &argc) != LN_SUCCESS) {
         printf("ERROR: Failed to parse parameters\n");
         return ERRCODE_FAIL;
     }
@@ -1837,14 +1380,14 @@ static uint32_t ln_switch_expression_at(uint8_t *para, uint32_t paraLen, char *s
 /* AT^LN_API_ERROR - 推送协议错误 */
 static uint32_t ln_api_error_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
 
     uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
     uint32_t argc = 0;
     const char *message = ""; // 默认空消息
 
     if (para != NULL && paraLen > 0) {
-        if (ConvertCmdToParam(para, paraLen, argv, &argc) != VAD_SUCCESS) {
+        if (ConvertCmdToParam(para, paraLen, argv, &argc) != LN_SUCCESS) {
             printf("ERROR: Failed to parse parameters\n");
             return ERRCODE_FAIL;
         }
@@ -1867,13 +1410,13 @@ static uint32_t ln_api_error_at(uint8_t *para, uint32_t paraLen, char *strCommon
 //AT^LN_NOTIFY_DOWNLOAD_SUCCESS=0/1
 static uint32_t ln_download_success_notification_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
     int type = 0;
     uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
     uint32_t argc = 0;
 
     if (para != NULL && paraLen > 0) {
-        if (ConvertCmdToParam(para, paraLen, argv, &argc) != VAD_SUCCESS) {
+        if (ConvertCmdToParam(para, paraLen, argv, &argc) != LN_SUCCESS) {
             printf("ERROR: Failed to parse parameters\n");
             return ERRCODE_FAIL;
         }
@@ -1895,7 +1438,7 @@ static uint32_t ln_download_success_notification_at(uint8_t *para, uint32_t para
 /* AT^LN_TIMELINE_GET_STATUS - 获取时间线状态 */
 static uint32_t ln_timeline_get_status_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(strCommonCmd);
     do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_TIMELINE_GET_STATUS, NULL, 0);
     return ERRCODE_SUCC;
 }
@@ -1903,9 +1446,9 @@ static uint32_t ln_timeline_get_status_at(uint8_t *para, uint32_t paraLen, char
 /* AT^LN_WS_AUDIO_INIT - 初始化WebSocket音频 */
 static uint32_t ln_ws_audio_init_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para);
-    VAD_UNUSED(paraLen);
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para);
+    LN_UNUSED(paraLen);
+    LN_UNUSED(strCommonCmd);
     
     printf("[LN][WS_AUDIO_INIT] Initializing WebSocket audio...\n");
     
@@ -1918,9 +1461,9 @@ static uint32_t ln_ws_audio_init_at(uint8_t *para, uint32_t paraLen, char *strCo
 /* AT^LN_WS_AUDIO_DEINIT - 反初始化WebSocket音频 */
 static uint32_t ln_ws_audio_deinit_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para);
-    VAD_UNUSED(paraLen);
-    VAD_UNUSED(strCommonCmd);
+    LN_UNUSED(para);
+    LN_UNUSED(paraLen);
+    LN_UNUSED(strCommonCmd);
     
     printf("[LN][WS_AUDIO_DEINIT] Deinitializing WebSocket audio...\n");
     
@@ -1931,15 +1474,6 @@ static uint32_t ln_ws_audio_deinit_at(uint8_t *para, uint32_t paraLen, char *str
 }
 
 static const at_cmd_table_t g_ln_api_cmd[] = {
-    /* ----------------------------------- VAD ---------------------------------- */
-    {(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 *)"显示帮助信息"},
     /* ---------------------------------- 雷鸟API --------------------------------- */
     {(char *)"AT^LN_GET_TOKEN", ln_get_device_token_at, (char *)"获取雷鸟设备Token: AT^LN_GET_TOKEN[=deviceId,deviceIccid,deviceModel,deviceVersion]"},
     {(char *)"AT^LN_CLEAR_TOKEN", ln_clear_device_token_at, (char *)"清除雷鸟设备Token: AT^LN_CLEAR_TOKEN"},
diff --git a/code/sdk/zhihui/minimp3/CMakeLists.txt b/code/sdk/zhihui/minimp3/CMakeLists.txt
new file mode 100644 (file)
index 0000000..dd9f32f
--- /dev/null
@@ -0,0 +1,57 @@
+#===============================================================================
+# @brief    imsl_ln component cmake file
+# Copyright (c) @CompanyNameMagicTag 2023-2023. All rights reserved.
+#===============================================================================
+set(COMPONENT_NAME "minimp3")
+
+# 源文件:系统适配层实现
+set(SOURCES
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/mp3_decoder.c
+)
+
+# 公共头文件路径(供其他模块引用)
+set(PUBLIC_HEADER
+    ${CMAKE_CURRENT_SOURCE_DIR}/include  
+)
+
+# 公共宏定义(可选)
+set(PUBLIC_DEFINES
+    MINI_MP3=1
+)
+
+# 私有头文件路径(仅当前组件内部使用)
+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目录)
+
+# 构建组件
+build_component()
\ No newline at end of file
similarity index 52%
rename from code/sdk/zhihui/utils/mp3_decoder.h
rename to code/sdk/zhihui/minimp3/include/mp3_decoder.h
index f7e0a21..62bb8ba 100644 (file)
@@ -30,89 +30,77 @@ typedef struct mp3_decoder_s mp3_decoder_t;
  */
 typedef void (*mp3_pcm_callback_t)(const int16_t *pcm_data, int samples, int channels, int sample_rate);
 
+/* ==================== 核心 API ==================== */
+
 /**
- * @brief 创建 MP3 解码器
+ * @brief 创建/初始化 MP3 流式解码器(优化版)
  * @return mp3_decode_error_t 错误码
+ * @note 内存占用: ~20KB (16KB流缓冲 + 4.6KB PCM缓冲)
  */
 mp3_decode_error_t mp3_decoder_create(void);
 
 /**
- * @brief 销毁 MP3 解码器
+ * @brief 销毁 MP3 解码器,释放所有资源
  */
 void mp3_decoder_destroy(void);
 
 /**
- * @brief 从文件加载 MP3 数据
- * @param filename MP3 文件路径
- * @return mp3_decode_error_t 错误码
+ * @brief 设置 PCM 数据回调函数
+ * @param callback PCM 回调函数
  */
-mp3_decode_error_t mp3_decoder_load_file(const char *filename);
+void mp3_decoder_set_callback(mp3_pcm_callback_t callback);
 
-/**
- * @brief 异步解码 MP3 数据(使用队列机制)
- * @param callback PCM 数据回调函数,可为 NULL
- * @return mp3_decode_error_t 错误码
- * @note 此函数启动异步解码,不等待完成。解码在后台线程中进行。
- */
-mp3_decode_error_t mp3_decoder_decode(mp3_pcm_callback_t callback);
+/* ==================== 流式解码 API ==================== */
 
 /**
- * @brief å\90\91 MP3 é\98\9få\88\97å\86\99å\85¥æ\95°æ\8d®
+ * @brief å\96\82å\85¥ MP3 æ\95°æ\8d®å\88°æµ\81ç¼\93å\86²å\8cºå¹¶è\87ªå\8a¨è§£ç \81
  * @param data MP3 数据
  * @param data_size 数据大小
- * @param is_last_packet 是否为最后一个数据包
  * @return mp3_decode_error_t 错误码
+ * @note 支持流式解码,边喂数据边解码,无文件大小限制
+ * 
+ * 使用示例:
+ *   while (read_chunk_from_source(chunk, size)) {
+ *       mp3_decoder_feed_data(chunk, size);
+ *   }
  */
-mp3_decode_error_t mp3_decoder_write_data(const uint8_t *data, size_t data_size, bool is_last_packet);
-
-/**
- * @brief 设置 PCM 回调函数
- * @param callback PCM 回调函数
- */
-void mp3_decoder_set_callback(mp3_pcm_callback_t callback);
+mp3_decode_error_t mp3_decoder_feed_data(const uint8_t *data, size_t data_size);
 
 /**
- * @brief 等待解码完成
- * @param timeout_ms 超时时间(毫秒),0表示无限等待
+ * @brief 刷新解码器,处理剩余数据
  * @return mp3_decode_error_t 错误码
+ * @note 在完成数据喂入后调用
  */
-mp3_decode_error_t mp3_decoder_wait_finish(uint32_t timeout_ms);
+mp3_decode_error_t mp3_decoder_flush(void);
 
 /**
- * @brief 初始化 MP3 解码器(简化接口)
- * @param callback PCM 数据回调函数,可为 NULL
- * @return mp3_decode_error_t 错误码
- */
-mp3_decode_error_t mp3_decoder_init_decode(mp3_pcm_callback_t callback);
-
-/**
- * @brief 写入 MP3 数据(简化接口)
- * @param data MP3 数据
- * @param data_size 数据大小
- * @param is_last 是否为最后一块数据
+ * @brief 流式解码 MP3 文件(优化版 - 边读边解码)
+ * @param filename MP3 文件路径
+ * @param callback PCM 回调函数
  * @return mp3_decode_error_t 错误码
+ * @note 一键解码整个文件,无文件大小限制,内存占用低
+ * 
+ * 使用示例:
+ *   mp3_decoder_decode_file_stream("music.mp3", pcm_callback);
  */
-mp3_decode_error_t mp3_decoder_write(const uint8_t *data, size_t data_size, bool is_last);
+mp3_decode_error_t mp3_decoder_decode_file_stream(
+    const char *filename,
+    void (*callback)(const int16_t *pcm_data, int samples, int channels, int sample_rate));
 
-/**
- * @brief 检查解码是否完成(简化接口)
- * @return bool true 表示解码完成,false 表示还在进行中
- */
-bool mp3_decoder_is_finished(void);
+/* ==================== 辅助 API ==================== */
 
 /**
- * @brief 获取解码进度(简化接口)
- * @param frames 输出已解码帧数
- * @param samples 输出已解码样本数
+ * @brief 初始化 MP3 解码器并设置回调(便捷接口)
+ * @param callback PCM 数据回调函数,可为 NULL
  * @return mp3_decode_error_t 错误码
  */
-mp3_decode_error_t mp3_decoder_get_progress(int *frames, int *samples);
+mp3_decode_error_t mp3_decoder_init_decode(mp3_pcm_callback_t callback);
 
 /**
- * @brief 停止解码(简化接口)
+ * @brief 重置解码器状态
  * @return mp3_decode_error_t 错误码
  */
-mp3_decode_error_t mp3_decoder_stop(void);
+mp3_decode_error_t mp3_decoder_reset(void);
 
 /**
  * @brief 获取解码统计信息
@@ -133,12 +121,6 @@ void mp3_decoder_get_stats(int *frames, int *samples, int *duration_ms);
  */
 int save_pcm_as_wav(const char *filename, const int16_t *pcm_data, int samples, int channels, int sample_rate);
 
-/**
- * @brief 示例用法函数
- * @param mp3_filename MP3 文件路径
- * @return int 成功返回 0,失败返回 -1
- */
-int mp3_decoder_example(const char *mp3_filename);
 
 #ifdef __cplusplus
 }
diff --git a/code/sdk/zhihui/minimp3/src/mp3_decoder.c b/code/sdk/zhihui/minimp3/src/mp3_decoder.c
new file mode 100644 (file)
index 0000000..72a8877
--- /dev/null
@@ -0,0 +1,487 @@
+#define MINIMP3_IMPLEMENTATION
+#include "minimp3.h"
+#include "custom_utils.h"
+#include "thread_init.h"
+#include "osal_msgqueue.h"
+#include "cmsis_os2.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+// 定义缓冲区大小 - 优化后使用流式解码
+#define MP3_STREAM_BUFFER_SIZE (16 * 1024)  // 16KB 流式缓冲区(优化:从1MB降至16KB)
+#define PCM_BUFFER_SIZE MINIMP3_MAX_SAMPLES_PER_FRAME
+
+// 文件读取块大小
+#define MP3_FILE_CHUNK_SIZE (4 * 1024)  // 4KB per read
+
+// 错误码定义
+typedef enum {
+    MP3_DECODE_SUCCESS = 0,
+    MP3_DECODE_ERROR_FILE_OPEN,
+    MP3_DECODE_ERROR_FILE_READ,
+    MP3_DECODE_ERROR_INVALID_FORMAT,
+    MP3_DECODE_ERROR_MEMORY,
+    MP3_DECODE_ERROR_UNKNOWN
+} mp3_decode_error_t;
+
+// MP3 流式解码器结构体(优化版本)
+typedef struct {
+    mp3dec_t decoder;  // 解码器状态
+    mp3dec_frame_info_t frame_info;
+    
+    // 流式缓冲区(16KB)
+    uint8_t stream_buffer[MP3_STREAM_BUFFER_SIZE];
+    size_t stream_buffer_used;  // 当前使用的字节数
+    
+    // PCM 输出缓冲区
+    int16_t *pcm_buffer;
+    
+    // 统计信息
+    int total_samples_decoded;
+    int total_frames_decoded;
+    
+    // 回调函数
+    void (*pcm_callback)(const int16_t *pcm_data, int samples, int channels, int sample_rate);
+    
+    // 状态标志
+    bool is_initialized;
+} mp3_decoder_t;
+
+// 全局解码器实例
+static mp3_decoder_t *g_mp3_decoder = NULL;
+
+/**
+ * @brief 尝试从流缓冲区解码一帧MP3数据
+ * @return 解码的样本数,0表示需要更多数据,-1表示错误
+ */
+static int mp3_decoder_try_decode_frame(void)
+{
+    if (!g_mp3_decoder || !g_mp3_decoder->is_initialized) {
+        return -1;
+    }
+    
+    if (g_mp3_decoder->stream_buffer_used == 0) {
+        return 0;  // 需要更多数据
+    }
+    
+    int samples = mp3dec_decode_frame(&g_mp3_decoder->decoder,
+                                     g_mp3_decoder->stream_buffer,
+                                     g_mp3_decoder->stream_buffer_used,
+                                     g_mp3_decoder->pcm_buffer,
+                                     &g_mp3_decoder->frame_info);
+    
+    if (samples > 0) {
+        // 解码成功
+        g_mp3_decoder->total_frames_decoded++;
+        g_mp3_decoder->total_samples_decoded += samples;
+        
+        // 打印第一帧信息
+        if (g_mp3_decoder->total_frames_decoded == 1) {
+            printf("[MP3] First frame decoded:\n");
+            printf("  Channels: %d\n", g_mp3_decoder->frame_info.channels);
+            printf("  Sample Rate: %d Hz\n", g_mp3_decoder->frame_info.hz);
+            printf("  Bitrate: %d kbps\n", g_mp3_decoder->frame_info.bitrate_kbps);
+            printf("  Layer: %d\n", g_mp3_decoder->frame_info.layer);
+        }
+        
+        // 调用回调函数
+        if (g_mp3_decoder->pcm_callback) {
+            g_mp3_decoder->pcm_callback(g_mp3_decoder->pcm_buffer, samples,
+                                       g_mp3_decoder->frame_info.channels,
+                                       g_mp3_decoder->frame_info.hz);
+        }
+        
+        // 移除已解码的数据
+        size_t frame_bytes = g_mp3_decoder->frame_info.frame_bytes;
+        if (frame_bytes > 0 && frame_bytes <= g_mp3_decoder->stream_buffer_used) {
+            g_mp3_decoder->stream_buffer_used -= frame_bytes;
+            if (g_mp3_decoder->stream_buffer_used > 0) {
+                memmove(g_mp3_decoder->stream_buffer,
+                       g_mp3_decoder->stream_buffer + frame_bytes,
+                       g_mp3_decoder->stream_buffer_used);
+            }
+        }
+    } else if (samples == 0) {
+        // 需要更多数据,尝试跳过一些字节
+        if (g_mp3_decoder->stream_buffer_used > 0) {
+            g_mp3_decoder->stream_buffer_used--;
+            if (g_mp3_decoder->stream_buffer_used > 0) {
+                memmove(g_mp3_decoder->stream_buffer,
+                       g_mp3_decoder->stream_buffer + 1,
+                       g_mp3_decoder->stream_buffer_used);
+            }
+        }
+    }
+    
+    return samples;
+}
+
+/**
+ * @brief 创建/初始化 MP3 流式解码器(优化版)
+ * @return mp3_decode_error_t 错误码
+ */
+mp3_decode_error_t mp3_decoder_create(void)
+{
+    printf("[MP3] Creating stream decoder...\n");
+    
+    // 检查是否已经创建
+    if (g_mp3_decoder && g_mp3_decoder->is_initialized) {
+        printf("[MP3] Decoder already initialized\n");
+        return MP3_DECODE_SUCCESS;
+    }
+    
+    // 分配解码器结构体内存(包含16KB流缓冲区)
+    g_mp3_decoder = (mp3_decoder_t*)malloc(sizeof(mp3_decoder_t));
+    if (!g_mp3_decoder) {
+        printf("[MP3] Error: Failed to allocate decoder memory (%zu bytes)\n", sizeof(mp3_decoder_t));
+        return MP3_DECODE_ERROR_MEMORY;
+    }
+    
+    // 清零结构体
+    memset(g_mp3_decoder, 0, sizeof(mp3_decoder_t));
+    
+    // 初始化 minimp3 解码器
+    mp3dec_init(&g_mp3_decoder->decoder);
+    
+    // 分配 PCM 输出缓冲区(4.6KB)
+    g_mp3_decoder->pcm_buffer = (int16_t*)malloc(PCM_BUFFER_SIZE * sizeof(int16_t));
+    if (!g_mp3_decoder->pcm_buffer) {
+        printf("[MP3] Error: Failed to allocate PCM buffer (%zu bytes)\n", PCM_BUFFER_SIZE * sizeof(int16_t));
+        free(g_mp3_decoder);
+        g_mp3_decoder = NULL;
+        return MP3_DECODE_ERROR_MEMORY;
+    }
+    
+    // 初始化状态
+    g_mp3_decoder->stream_buffer_used = 0;
+    g_mp3_decoder->total_samples_decoded = 0;
+    g_mp3_decoder->total_frames_decoded = 0;
+    g_mp3_decoder->pcm_callback = NULL;
+    g_mp3_decoder->is_initialized = true;
+    
+    printf("[MP3] Decoder created successfully\n");
+    printf("[MP3] Memory usage: ~%zu KB (structure=%zu, PCM buffer=%zu)\n",
+           (sizeof(mp3_decoder_t) + PCM_BUFFER_SIZE * sizeof(int16_t)) / 1024,
+           sizeof(mp3_decoder_t) / 1024,
+           (PCM_BUFFER_SIZE * sizeof(int16_t)) / 1024);
+    
+    return MP3_DECODE_SUCCESS;
+}
+
+/**
+ * @brief 销毁 MP3 流式解码器
+ */
+void mp3_decoder_destroy(void)
+{
+    if (!g_mp3_decoder) {
+        printf("[MP3] Decoder not initialized\n");
+        return;
+    }
+    
+    printf("[MP3] Destroying decoder...\n");
+    
+    // 释放 PCM 缓冲区
+    if (g_mp3_decoder->pcm_buffer) {
+        free(g_mp3_decoder->pcm_buffer);
+        g_mp3_decoder->pcm_buffer = NULL;
+    }
+    
+    // 释放解码器结构体
+    free(g_mp3_decoder);
+    g_mp3_decoder = NULL;
+    
+    printf("[MP3] Decoder destroyed\n");
+}
+
+/**
+ * @brief 喂入 MP3 数据到流缓冲区并尝试解码(流式API)
+ * @param data MP3 数据
+ * @param data_size 数据大小
+ * @return mp3_decode_error_t 错误码
+ */
+mp3_decode_error_t mp3_decoder_feed_data(const uint8_t *data, size_t data_size)
+{
+    if (!g_mp3_decoder || !g_mp3_decoder->is_initialized) {
+        printf("[MP3] Error: Decoder not initialized\n");
+        return MP3_DECODE_ERROR_UNKNOWN;
+    }
+    
+    if (!data || data_size == 0) {
+        return MP3_DECODE_SUCCESS;
+    }
+    
+    // 检查缓冲区空间
+    size_t available = MP3_STREAM_BUFFER_SIZE - g_mp3_decoder->stream_buffer_used;
+    if (data_size > available) {
+        printf("[MP3] Warning: Stream buffer full, processing data in chunks\n");
+        data_size = available;  // 只接受能容纳的数据
+    }
+    
+    // 将数据追加到流缓冲区
+    memcpy(g_mp3_decoder->stream_buffer + g_mp3_decoder->stream_buffer_used,
+           data, data_size);
+    g_mp3_decoder->stream_buffer_used += data_size;
+    
+    // 尝试解码所有可能的帧
+    int decoded_frames = 0;
+    while (1) {
+        int samples = mp3_decoder_try_decode_frame();
+        if (samples > 0) {
+            decoded_frames++;
+        } else if (samples == 0) {
+            // 需要更多数据
+            break;
+        } else {
+            // 解码错误
+            break;
+        }
+    }
+    
+    if (decoded_frames > 0) {
+        printf("[MP3] Decoded %d frames from %zu bytes input\n", decoded_frames, data_size);
+    }
+    
+    return MP3_DECODE_SUCCESS;
+}
+
+/**
+ * @brief 刷新解码器,处理剩余数据
+ * @return mp3_decode_error_t 错误码
+ */
+mp3_decode_error_t mp3_decoder_flush(void)
+{
+    if (!g_mp3_decoder || !g_mp3_decoder->is_initialized) {
+        return MP3_DECODE_ERROR_UNKNOWN;
+    }
+    
+    printf("[MP3] Flushing decoder, remaining buffer: %zu bytes\n", 
+           g_mp3_decoder->stream_buffer_used);
+    
+    // 清空缓冲区
+    g_mp3_decoder->stream_buffer_used = 0;
+    
+    printf("[MP3] Total decoded: %d frames, %d samples\n",
+           g_mp3_decoder->total_frames_decoded,
+           g_mp3_decoder->total_samples_decoded);
+    
+    return MP3_DECODE_SUCCESS;
+}
+
+/**
+ * @brief 设置 PCM 回调函数
+ * @param callback PCM 回调函数
+ */
+void mp3_decoder_set_callback(void (*callback)(const int16_t *pcm_data, int samples, int channels, int sample_rate))
+{
+    if (g_mp3_decoder && g_mp3_decoder->is_initialized) {
+        g_mp3_decoder->pcm_callback = callback;
+        printf("[MP3] PCM callback set\n");
+    }
+}
+
+/**
+ * @brief 流式解码 MP3 文件(优化版 - 边读边解码)
+ * @param filename MP3 文件路径
+ * @param callback PCM 回调函数
+ * @return mp3_decode_error_t 错误码
+ */
+mp3_decode_error_t mp3_decoder_decode_file_stream(
+    const char *filename,
+    void (*callback)(const int16_t *pcm_data, int samples, int channels, int sample_rate))
+{
+    if (!filename) {
+        return MP3_DECODE_ERROR_UNKNOWN;
+    }
+    
+    // 确保解码器已创建
+    if (!g_mp3_decoder || !g_mp3_decoder->is_initialized) {
+        mp3_decode_error_t ret = mp3_decoder_create();
+        if (ret != MP3_DECODE_SUCCESS) {
+            return ret;
+        }
+    }
+    
+    // 设置回调
+    g_mp3_decoder->pcm_callback = callback;
+    
+    // 重置统计
+    g_mp3_decoder->stream_buffer_used = 0;
+    g_mp3_decoder->total_samples_decoded = 0;
+    g_mp3_decoder->total_frames_decoded = 0;
+    
+    // 打开文件
+    FILE *file = fopen(filename, "rb");
+    if (!file) {
+        printf("[MP3] Error: Cannot open file %s: %s\n", filename, strerror(errno));
+        return MP3_DECODE_ERROR_FILE_OPEN;
+    }
+    
+    printf("[MP3] Streaming decode: %s\n", filename);
+    
+    // 流式读取并解码
+    uint8_t chunk[MP3_FILE_CHUNK_SIZE];
+    size_t bytes_read;
+    size_t total_bytes = 0;
+    
+    while ((bytes_read = fread(chunk, 1, MP3_FILE_CHUNK_SIZE, file)) > 0) {
+        total_bytes += bytes_read;
+        mp3_decoder_feed_data(chunk, bytes_read);
+    }
+    
+    fclose(file);
+    
+    // 刷新剩余数据
+    mp3_decoder_flush();
+    
+    printf("[MP3] File decoded successfully\n");
+    printf("[MP3] Total read: %zu bytes, decoded: %d frames, %d samples\n",
+           total_bytes, g_mp3_decoder->total_frames_decoded, g_mp3_decoder->total_samples_decoded);
+    
+    return MP3_DECODE_SUCCESS;
+}
+
+
+/**
+ * @brief 获取解码统计信息
+ * @param frames 输出帧数
+ * @param samples 输出样本数
+ * @param duration_ms 输出时长(毫秒)
+ */
+void mp3_decoder_get_stats(int *frames, int *samples, int *duration_ms)
+{
+    if (g_mp3_decoder && g_mp3_decoder->is_initialized) {
+        if (frames) *frames = g_mp3_decoder->total_frames_decoded;
+        if (samples) *samples = g_mp3_decoder->total_samples_decoded;
+        if (duration_ms && g_mp3_decoder->frame_info.hz > 0) {
+            *duration_ms = (g_mp3_decoder->total_samples_decoded * 1000) / 
+                          (g_mp3_decoder->frame_info.hz * g_mp3_decoder->frame_info.channels);
+        }
+    }
+}
+
+/**
+ * @brief 将 PCM 数据保存为 WAV 文件
+ * @param filename 输出文件名
+ * @param pcm_data PCM 数据
+ * @param samples 样本数
+ * @param channels 声道数
+ * @param sample_rate 采样率
+ * @return int 成功返回 0,失败返回 -1
+ */
+int save_pcm_as_wav(const char *filename, const int16_t *pcm_data, int samples, int channels, int sample_rate)
+{
+    FILE *file = fopen(filename, "wb");
+    if (!file) {
+        printf("Error: Cannot create WAV file %s\n", filename);
+        return -1;
+    }
+    
+    // WAV 文件头
+    int data_size = samples * channels * sizeof(int16_t);
+    int file_size = 36 + data_size;
+    
+    // RIFF 头
+    fwrite("RIFF", 1, 4, file);
+    fwrite(&file_size, 4, 1, file);
+    fwrite("WAVE", 1, 4, file);
+    
+    // fmt 块
+    fwrite("fmt ", 1, 4, file);
+    int fmt_size = 16;
+    fwrite(&fmt_size, 4, 1, file);
+    int16_t audio_format = 1; // PCM
+    fwrite(&audio_format, 2, 1, file);
+    fwrite(&channels, 2, 1, file);
+    fwrite(&sample_rate, 4, 1, file);
+    int byte_rate = sample_rate * channels * sizeof(int16_t);
+    fwrite(&byte_rate, 4, 1, file);
+    int16_t block_align = channels * sizeof(int16_t);
+    fwrite(&block_align, 2, 1, file);
+    int16_t bits_per_sample = 16;
+    fwrite(&bits_per_sample, 2, 1, file);
+    
+    // data 块
+    fwrite("data", 1, 4, file);
+    fwrite(&data_size, 4, 1, file);
+    fwrite(pcm_data, sizeof(int16_t), samples * channels, file);
+    
+    fclose(file);
+    printf("WAV file saved: %s (%d samples)\n", filename, samples);
+    return 0;
+}
+
+// 示例回调函数:打印 PCM 数据信息
+void pcm_callback_example(const int16_t *pcm_data, int samples, int channels, int sample_rate)
+{
+    static int call_count = 0;
+    call_count++;
+    
+    if (call_count % 100 == 1) { // 每100帧打印一次
+        printf("Frame %d: %d samples, %d channels, %d Hz\n", 
+               call_count, samples, channels, sample_rate);
+    }
+}
+
+// 示例回调函数:保存 PCM 数据到文件
+void pcm_callback_save_to_file(const int16_t *pcm_data, int samples, int channels, int sample_rate)
+{
+    static FILE *pcm_file = NULL;
+    static int frame_count = 0;
+    
+    if (!pcm_file) {
+        pcm_file = fopen("output.pcm", "wb");
+        if (!pcm_file) {
+            printf("Error: Cannot create PCM file\n");
+            return;
+        }
+        printf("PCM data will be saved to output.pcm\n");
+    }
+    
+    fwrite(pcm_data, sizeof(int16_t), samples * channels, pcm_file);
+    frame_count++;
+    
+    if (frame_count % 100 == 0) {
+        printf("Saved %d frames to PCM file\n", frame_count);
+    }
+}
+
+
+/**
+ * @brief 初始化 MP3 解码器并设置回调(简化接口)
+ * @param callback PCM 数据回调函数,可为 NULL
+ * @return mp3_decode_error_t 错误码
+ */
+mp3_decode_error_t mp3_decoder_init_decode(void (*callback)(const int16_t *pcm_data, int samples, int channels, int sample_rate))
+{
+    mp3_decode_error_t ret = mp3_decoder_create();
+    if (ret != MP3_DECODE_SUCCESS) {
+        return ret;
+    }
+    
+    if (callback) {
+        mp3_decoder_set_callback(callback);
+    }
+    
+    return MP3_DECODE_SUCCESS;
+}
+
+/**
+ * @brief 重置解码器状态(简化接口)
+ * @return mp3_decode_error_t 错误码
+ */
+mp3_decode_error_t mp3_decoder_reset(void)
+{
+    if (!g_mp3_decoder || !g_mp3_decoder->is_initialized) {
+        return MP3_DECODE_ERROR_UNKNOWN;
+    }
+    
+    g_mp3_decoder->stream_buffer_used = 0;
+    g_mp3_decoder->total_samples_decoded = 0;
+    g_mp3_decoder->total_frames_decoded = 0;
+    
+    printf("[MP3] Decoder reset\n");
+    return MP3_DECODE_SUCCESS;
+}
diff --git a/code/sdk/zhihui/utils/README_MP3_DECODER.md b/code/sdk/zhihui/utils/README_MP3_DECODER.md
deleted file mode 100644 (file)
index 09fb756..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-# MP3 解码器使用说明
-
-基于 minimp3 库实现的轻量级 MP3 解码器。
-
-## 文件说明
-
-- `minimp3.h` - MinimP3 库(单头文件库)
-- `mp3_decoder.h` - MP3 解码器头文件
-- `mp3_decoder.c` - MP3 解码器实现(支持线程和队列)
-- `mp3_thread_example.c` - 线程和队列使用示例
-- `mp3_simple_example.c` - 简化接口使用示例
-
-## 编译方法
-
-### 方法1:编译简化接口示例程序
-```bash
-# 编译简化接口示例程序
-gcc -DMINIMP3_IMPLEMENTATION -DMP3_DECODER_SIMPLE_TEST -o mp3_simple_example mp3_decoder.c mp3_simple_example.c
-
-# 或者分步编译
-gcc -DMINIMP3_IMPLEMENTATION -c mp3_decoder.c
-gcc -DMP3_DECODER_SIMPLE_TEST -c mp3_simple_example.c
-gcc -o mp3_simple_example mp3_decoder.o mp3_simple_example.o
-```
-
-### 方法2:编译线程示例程序
-```bash
-# 编译线程示例程序
-gcc -DMINIMP3_IMPLEMENTATION -DMP3_DECODER_THREAD_TEST -o mp3_thread_example mp3_decoder.c mp3_thread_example.c
-```
-
-### 方法3:编译基本示例程序
-```bash
-# 编译基本示例程序(在 mp3_decoder.c 中定义)
-gcc -DMINIMP3_IMPLEMENTATION -DMP3_DECODER_TEST -o mp3_example mp3_decoder.c
-```
-
-## 使用方法
-
-### 1. 简化接口示例(推荐)
-```bash
-# 使用简化接口解码
-./mp3_simple_example test.mp3 simple
-
-# 流式解码模式
-./mp3_simple_example test.mp3 stream
-```
-
-### 2. 线程和队列示例
-```bash
-# 使用线程和队列机制解码
-./mp3_thread_example test.mp3 thread
-
-# 流式数据写入队列
-./mp3_thread_example test.mp3 stream
-```
-
-### 3. 基本示例
-```bash
-# 运行基本示例程序
-./mp3_example test.mp3
-```
-
-## 编程接口使用
-
-### 简化接口用法(推荐)
-```c
-#include "mp3_decoder.h"
-
-// PCM 回调函数
-void my_pcm_callback(const int16_t *pcm_data, int samples, int channels, int sample_rate) {
-    printf("收到 PCM 数据: %d 样本, %d 声道, %d Hz\n", samples, channels, sample_rate);
-}
-
-int main() {
-    // 1. 创建解码器
-    mp3_decoder_t *decoder = mp3_decoder_create();
-    if (!decoder) {
-        printf("创建解码器失败\n");
-        return -1;
-    }
-    
-    // 2. 初始化解码
-    mp3_decoder_init_decode(decoder, my_pcm_callback);
-    
-    // 3. 写入 MP3 数据
-    uint8_t mp3_data[] = { /* MP3 数据 */ };
-    mp3_decoder_write(decoder, mp3_data, sizeof(mp3_data), true);
-    
-    // 4. 等待解码完成
-    while (!mp3_decoder_is_finished(decoder)) {
-        osDelay(100);
-        
-        // 检查进度
-        int frames, samples;
-        mp3_decoder_get_progress(decoder, &frames, &samples);
-        printf("进度: %d 帧, %d 样本\n", frames, samples);
-    }
-    
-    // 5. 清理
-    mp3_decoder_destroy(decoder);
-    return 0;
-}
-```
-
-### 流式解码用法
-```c
-#include "mp3_decoder.h"
-
-int main() {
-    mp3_decoder_t *decoder = mp3_decoder_create();
-    mp3_decoder_init_decode(decoder, my_pcm_callback);
-    
-    // 打开文件
-    FILE *file = fopen("test.mp3", "rb");
-    uint8_t buffer[4096];
-    size_t bytes_read;
-    
-    // 流式读取并写入
-    while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) {
-        bool is_last = (bytes_read < sizeof(buffer));
-        mp3_decoder_write(decoder, buffer, bytes_read, is_last);
-        
-        // 检查是否完成
-        if (mp3_decoder_is_finished(decoder)) {
-            break;
-        }
-    }
-    
-    fclose(file);
-    mp3_decoder_destroy(decoder);
-    return 0;
-}
-```
-
-### 流式数据写入
-```c
-#include "mp3_decoder.h"
-
-int main() {
-    // 创建解码器
-    mp3_decoder_t *decoder = mp3_decoder_create();
-    if (!decoder) {
-        printf("创建解码器失败\n");
-        return -1;
-    }
-    
-    // 设置回调函数
-    mp3_decoder_set_callback(decoder, my_pcm_callback);
-    
-    // 打开 MP3 文件
-    FILE *file = fopen("test.mp3", "rb");
-    if (!file) {
-        printf("无法打开文件\n");
-        mp3_decoder_destroy(decoder);
-        return -1;
-    }
-    
-    // 流式读取并写入队列
-    uint8_t buffer[4096];
-    size_t bytes_read;
-    int packet_count = 0;
-    
-    while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) {
-        bool is_last = (bytes_read < sizeof(buffer));
-        
-        mp3_decode_error_t ret = mp3_decoder_write_data(decoder, buffer, bytes_read, is_last);
-        if (ret != MP3_DECODE_SUCCESS) {
-            printf("写入队列失败: %d\n", ret);
-            break;
-        }
-        
-        packet_count++;
-        printf("数据包 %d: %zu 字节%s\n", packet_count, bytes_read, is_last ? " (最后)" : "");
-    }
-    
-    fclose(file);
-    
-    // 等待解码完成
-    mp3_decode_error_t ret = mp3_decoder_wait_finish(decoder, 30000); // 30秒超时
-    if (ret != MP3_DECODE_SUCCESS) {
-        printf("解码未完成: %d\n", ret);
-    }
-    
-    // 清理
-    mp3_decoder_destroy(decoder);
-    return 0;
-}
-```
-
-### 自定义回调函数
-```c
-// 自定义 PCM 数据处理回调
-void my_pcm_callback(const int16_t *pcm_data, int samples, int channels, int sample_rate) {
-    printf("收到 PCM 数据: %d 样本, %d 声道, %d Hz\n", samples, channels, sample_rate);
-    
-    // 在这里处理 PCM 数据
-    // 例如:播放、保存、分析等
-}
-
-int main() {
-    mp3_decoder_t *decoder = mp3_decoder_create();
-    mp3_decoder_load_file(decoder, "test.mp3");
-    
-    // 使用自定义回调解码
-    mp3_decoder_decode(decoder, my_pcm_callback);
-    
-    mp3_decoder_destroy(decoder);
-    return 0;
-}
-```
-
-## API 参考
-
-### 主要函数
-
-#### `mp3_decoder_create()`
-创建 MP3 解码器实例。
-- **返回值**: 解码器指针,失败返回 NULL
-
-#### `mp3_decoder_destroy(decoder)`
-销毁解码器实例。
-- **参数**: `decoder` - 解码器指针
-
-#### `mp3_decoder_load_file(decoder, filename)`
-从文件加载 MP3 数据。
-- **参数**: 
-  - `decoder` - 解码器指针
-  - `filename` - MP3 文件路径
-- **返回值**: 错误码
-
-#### `mp3_decoder_decode(decoder, callback)`
-使用队列机制异步解码 MP3 数据。
-- **参数**:
-  - `decoder` - 解码器指针
-  - `callback` - PCM 数据回调函数,可为 NULL
-- **返回值**: 错误码
-- **注意**: 此函数启动异步解码,不等待完成。解码在后台线程中进行。
-
-#### `mp3_decoder_write_data(decoder, data, data_size, is_last_packet)`
-向 MP3 队列写入数据。
-- **参数**:
-  - `decoder` - 解码器指针
-  - `data` - MP3 数据
-  - `data_size` - 数据大小
-  - `is_last_packet` - 是否为最后一个数据包
-- **返回值**: 错误码
-
-#### `mp3_decoder_set_callback(decoder, callback)`
-设置 PCM 回调函数。
-- **参数**:
-  - `decoder` - 解码器指针
-  - `callback` - PCM 回调函数
-
-#### `mp3_decoder_wait_finish(decoder, timeout_ms)`
-等待解码完成。
-- **参数**:
-  - `decoder` - 解码器指针
-  - `timeout_ms` - 超时时间(毫秒),0表示无限等待
-- **返回值**: 错误码
-
-#### `mp3_decoder_get_stats(decoder, frames, samples, duration_ms)`
-获取解码统计信息。
-- **参数**:
-  - `decoder` - 解码器指针
-  - `frames` - 输出帧数(可为 NULL)
-  - `samples` - 输出样本数(可为 NULL)
-  - `duration_ms` - 输出时长毫秒(可为 NULL)
-
-### 错误码
-- `MP3_DECODE_SUCCESS` - 成功
-- `MP3_DECODE_ERROR_FILE_OPEN` - 文件打开失败
-- `MP3_DECODE_ERROR_FILE_READ` - 文件读取失败
-- `MP3_DECODE_ERROR_INVALID_FORMAT` - 无效格式
-- `MP3_DECODE_ERROR_MEMORY` - 内存分配失败
-- `MP3_DECODE_ERROR_UNKNOWN` - 未知错误
-
-## 特性
-
-- ✅ 支持 MP3 (MPEG-1 Layer 3) 解码
-- ✅ 支持单声道和立体声
-- ✅ 支持多种采样率 (8kHz, 11.025kHz, 12kHz, 16kHz, 22.05kHz, 24kHz, 32kHz, 44.1kHz, 48kHz)
-- ✅ 支持多种比特率
-- ✅ **线程安全解码** - 使用独立线程进行解码
-- ✅ **队列机制** - 支持流式数据输入
-- ✅ **异步解码** - 非阻塞式解码,立即返回
-- ✅ **后台处理** - 解码在独立线程中进行
-- ✅ 内存效率高
-- ✅ 支持 WAV 文件输出
-- ✅ 支持原始 PCM 数据输出
-- ✅ **实时流式解码** - 支持边接收边解码
-
-## 注意事项
-
-1. **内存管理**: 使用完毕后必须调用 `mp3_decoder_destroy()` 释放资源
-2. **线程安全**: 解码器使用独立线程,支持多实例并发使用
-3. **队列大小**: 默认队列大小为 10 个数据包,可根据需要调整
-4. **数据包大小**: 每个队列项最大 4KB,大数据需要分块传输
-5. **超时处理**: 队列操作有超时机制,避免死锁
-6. **格式支持**: 仅支持 MP3 格式,不支持其他音频格式
-7. **编译选项**: 必须定义 `MINIMP3_IMPLEMENTATION` 宏来包含 minimp3 实现
-8. **依赖库**: 需要 `custom_utils.h`、`thread_init.h`、`osal_msgqueue.h` 等系统库
-
-## 示例输出
-
-### 线程和队列模式
-```
-=== MP3 Decoder Thread Example ===
-MP3 decoder created successfully
-Thread handle: 0x20001234, Queue ID: 0x20005678
-MP3 queue created successfully, ID: 0x20005678
-Loaded MP3 file: test.mp3 (2048576 bytes)
-Starting MP3 decoding with queue mechanism...
-MP3 data written to queue: 4096 bytes
-MP3 data written to queue: 4096 bytes
-...
-All MP3 data written to queue: 500 packets
-MP3 decode thread started
-MP3 Info:
-  Channels: 2
-  Sample Rate: 44100 Hz
-  Bitrate: 128 kbps
-  Layer: 3
-Decoded packet: 4096 bytes, 1 frames so far
-...
-MP3 decode thread finished: 1234 total frames, 567890 total samples
-Decode finished successfully
-
-Decoding Statistics:
-  Total frames: 1234
-  Total samples: 567890
-  Duration: 12876 ms (12.88 seconds)
-=== Thread Example completed ===
-```
-
-### 流式模式
-```
-=== MP3 Decoder Stream Example ===
-MP3 decoder created successfully
-Thread handle: 0x20001234, Queue ID: 0x20005678
-MP3 queue created successfully, ID: 0x20005678
-Streaming MP3 data to queue...
-Packet 1: 4096 bytes
-Packet 2: 4096 bytes
-...
-Packet 500: 1024 bytes (last)
-Waiting for decode to finish...
-MP3 decode thread started
-...
-MP3 decode thread finished: 1234 total frames, 567890 total samples
-Decode finished successfully
-
-Stream Decoding Statistics:
-  Total packets: 500
-  Total frames: 1234
-  Total samples: 567890
-  Duration: 12876 ms (12.88 seconds)
-=== Stream Example completed ===
-```
diff --git a/code/sdk/zhihui/utils/mp3_decoder.c b/code/sdk/zhihui/utils/mp3_decoder.c
deleted file mode 100644 (file)
index a246d50..0000000
+++ /dev/null
@@ -1,712 +0,0 @@
-#define MINIMP3_IMPLEMENTATION
-#include "minimp3.h"
-#include "custom_utils.h"
-#include "thread_init.h"
-#include "osal_msgqueue.h"
-#include "cmsis_os2.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
-#include <errno.h>
-
-// 定义缓冲区大小
-#define MP3_BUFFER_SIZE (1024 * 1024)  // 1MB 缓冲区
-#define PCM_BUFFER_SIZE MINIMP3_MAX_SAMPLES_PER_FRAME
-
-// MP3 队列相关定义
-#define MP3_QUEUE_SIZE 10
-#define MP3_QUEUE_ITEM_SIZE 1024  // 每个队列项的大小(4KB)
-
-// MP3 数据包结构
-typedef struct {
-    uint8_t data[MP3_QUEUE_ITEM_SIZE];
-    size_t data_size;
-    bool is_last_packet;
-} mp3_queue_item_t;
-
-// 错误码定义
-typedef enum {
-    MP3_DECODE_SUCCESS = 0,
-    MP3_DECODE_ERROR_FILE_OPEN,
-    MP3_DECODE_ERROR_FILE_READ,
-    MP3_DECODE_ERROR_INVALID_FORMAT,
-    MP3_DECODE_ERROR_MEMORY,
-    MP3_DECODE_ERROR_UNKNOWN
-} mp3_decode_error_t;
-
-// MP3 解码器结构体
-typedef struct {
-    mp3dec_t decoder;  // 直接使用结构体,不使用指针
-    mp3dec_frame_info_t frame_info;
-    uint8_t *mp3_buffer;
-    size_t mp3_buffer_size;
-    size_t mp3_data_size;
-    int16_t *pcm_buffer;
-    int total_samples_decoded;
-    int total_frames_decoded;
-    
-    // 线程和队列相关
-    uint32_t thread_handle;
-    uint32_t queue_id;
-    bool thread_running;
-    bool decode_finished;
-    void (*pcm_callback)(const int16_t *pcm_data, int samples, int channels, int sample_rate);
-} mp3_decoder_t;
-
-// 全局解码器实例(用于线程回调)
-static mp3_decoder_t *g_decoder_instance = NULL;
-
-// 动态全局解码器实例
-static mp3_decoder_t *g_mp3_decoder = NULL;
-static bool g_decoder_initialized = false;
-static bool g_decoder_created = false;        // 创建标志位
-static bool g_decoder_init_decode = false;    // 初始化解码标志位
-
-/**
- * @brief 创建 MP3 队列
- */
-static void mp3_queue_create(void)
-{
-    if (g_decoder_initialized && g_mp3_decoder) {
-        int ret = osal_msg_queue_create("mp3_queue", MP3_QUEUE_SIZE, 
-                                      &g_mp3_decoder->queue_id, 0, 
-                                      sizeof(mp3_queue_item_t));
-        if (ret != OSAL_SUCCESS) {
-            printf("Error: Failed to create MP3 queue\n");
-            g_mp3_decoder->queue_id = 0;
-        } else {
-            printf("MP3 queue created successfully, ID: 0x%x\n", g_mp3_decoder->queue_id);
-        }
-    }
-}
-
-/**
- * @brief MP3 解码线程主函数
- * @param argument 线程参数(未使用)
- */
-static void mp3_decode_thread(void *argument)
-{
-    // VAD_UNUSED(argument);
-    
-    if (!g_decoder_initialized || !g_mp3_decoder || !g_mp3_decoder->queue_id) {
-        printf("Error: Decoder not initialized or invalid queue in thread\n");
-        return;
-    }
-    
-    printf("MP3 decode thread started\n");
-    g_mp3_decoder->thread_running = true;
-    g_mp3_decoder->decode_finished = false;
-    
-    mp3_queue_item_t queue_item;
-    size_t current_offset = 0;
-    int frame_count = 0;
-    
-    while (g_mp3_decoder->thread_running && !g_mp3_decoder->decode_finished) {
-        // 从队列中读取 MP3 数据
-        int ret = osal_msg_queue_read_copy(g_mp3_decoder->queue_id, &queue_item, 
-                                          sizeof(mp3_queue_item_t), 1000); // 1秒超时
-
-         printf("osal_msg_queue_read_copy mp3_decode_thread\n");
-        if (ret == OSAL_SUCCESS) {
-            if (queue_item.is_last_packet) {
-                printf("Received last packet, finishing decode\n");
-                g_mp3_decoder->decode_finished = true;
-                break;
-            }
-            
-            // 解码接收到的数据
-            size_t data_offset = 0;
-            while (data_offset < queue_item.data_size) {
-                printf("mp3dec_decode_frame\n");
-                int samples = mp3dec_decode_frame(&g_mp3_decoder->decoder,
-                                                queue_item.data + data_offset,
-                                                queue_item.data_size - data_offset,
-                                                g_mp3_decoder->pcm_buffer,
-                                                &g_mp3_decoder->frame_info);
-                
-                if (samples > 0) {
-                    // 解码成功
-                    frame_count++;
-                    g_mp3_decoder->total_frames_decoded++;
-                    g_mp3_decoder->total_samples_decoded += samples;
-                    
-                    // 打印帧信息(仅第一帧)
-                    if (frame_count == 1) {
-                        printf("MP3 Info:\n");
-                        printf("  Channels: %d\n", g_mp3_decoder->frame_info.channels);
-                        printf("  Sample Rate: %d Hz\n", g_mp3_decoder->frame_info.hz);
-                        printf("  Bitrate: %d kbps\n", g_mp3_decoder->frame_info.bitrate_kbps);
-                        printf("  Layer: %d\n", g_mp3_decoder->frame_info.layer);
-                    }
-                    
-                    // 调用回调函数处理 PCM 数据
-                    if (g_mp3_decoder->pcm_callback) {
-                        g_mp3_decoder->pcm_callback(g_mp3_decoder->pcm_buffer, samples, 
-                                            g_mp3_decoder->frame_info.channels, g_mp3_decoder->frame_info.hz);
-                    }
-                    
-                    data_offset += g_mp3_decoder->frame_info.frame_bytes;
-                } else {
-                    // 解码失败,尝试跳过一些字节
-                    data_offset++;
-                    if (data_offset >= queue_item.data_size) {
-                        break;
-                    }
-                }
-            }
-            
-            printf("Decoded packet: %zu bytes, %d frames so far\n", 
-                   queue_item.data_size, frame_count);
-        } else {
-            printf("Error reading from queue: %d\n", ret);
-            continue;
-        }
-    }
-    
-    printf("MP3 decode thread finished: %d total frames, %d total samples\n", 
-           g_mp3_decoder->total_frames_decoded, g_mp3_decoder->total_samples_decoded);
-    g_mp3_decoder->thread_running = false;
-}
-
-/**
- * @brief 创建 MP3 解码器
- * @return mp3_decode_error_t 错误码
- */
-mp3_decode_error_t mp3_decoder_create(void)
-{
-    printf("mp3_decoder_create: Starting create process\n");
-    
-    // 检查是否已经创建
-    if (g_decoder_created) {
-        printf("MP3 decoder already created, skipping creation\n");
-        return MP3_DECODE_SUCCESS;
-    }
-    
-    if (g_decoder_initialized) {
-        printf("MP3 decoder already initialized\n");
-        return MP3_DECODE_SUCCESS;
-    }
-    
-    // 分配解码器结构体内存
-    g_mp3_decoder = (mp3_decoder_t*)malloc(sizeof(mp3_decoder_t));
-    if (!g_mp3_decoder) {
-        printf("mp3_decoder_create Error: Failed to allocate decoder memory\n");
-        return MP3_DECODE_ERROR_MEMORY;
-    }
-    
-    // 初始化解码器
-    mp3dec_init(&g_mp3_decoder->decoder);
-    
-    // 分配 MP3 缓冲区
-    g_mp3_decoder->mp3_buffer = (uint8_t*)malloc(MP3_BUFFER_SIZE);
-    if (!g_mp3_decoder->mp3_buffer) {
-        printf("mp3_decoder_create Error: Failed to allocate MP3 buffer\n");
-        free(g_mp3_decoder);
-        g_mp3_decoder = NULL;
-        return MP3_DECODE_ERROR_MEMORY;
-    }
-    
-    // 分配 PCM 缓冲区
-    g_mp3_decoder->pcm_buffer = (int16_t*)malloc(PCM_BUFFER_SIZE * sizeof(int16_t));
-    if (!g_mp3_decoder->pcm_buffer) {
-        printf("mp3_decoder_create Error: Failed to allocate PCM buffer\n");
-        free(g_mp3_decoder->mp3_buffer);
-        free(g_mp3_decoder);
-        g_mp3_decoder = NULL;
-        return MP3_DECODE_ERROR_MEMORY;
-    }
-    
-    g_mp3_decoder->mp3_buffer_size = MP3_BUFFER_SIZE;
-    g_mp3_decoder->mp3_data_size = 0;
-    g_mp3_decoder->total_samples_decoded = 0;
-    g_mp3_decoder->total_frames_decoded = 0;
-    
-    // 初始化线程和队列相关字段
-    g_mp3_decoder->thread_handle = 0;
-    g_mp3_decoder->queue_id = 0;
-    g_mp3_decoder->thread_running = false;
-    g_mp3_decoder->decode_finished = false;
-    g_mp3_decoder->pcm_callback = NULL;
-    
-    // 设置全局实例(用于队列创建回调)
-    g_decoder_instance = g_mp3_decoder;
-    g_decoder_initialized = true;
-    g_decoder_created = true;  // 设置创建标志位
-    
-    // 创建线程和队列
-    app_task_definition_ext_t task_cfg = {
-        .attr = {
-            .name = "mp3_decode_thread",
-            .stack_size = 8192,
-            .priority = TASK_PRIORITY_APP,
-        },
-        .func = mp3_decode_thread,
-        .task_handle = &g_mp3_decoder->thread_handle,
-        .create_queue_handle = mp3_queue_create,
-        .use_mem = USE_EXTEND_MEM,
-        .inst_args = NULL  // 不再需要传递参数
-    };
-    
-    errcode_t ret = uapi_create_osThread_custom_with_args(&task_cfg);
-    if (ret != ERRCODE_SUCC) {
-        printf("mp3_decoder_create Error: Failed to create MP3 decode thread: %d\n", ret);
-        free(g_mp3_decoder->mp3_buffer);
-        free(g_mp3_decoder->pcm_buffer);
-        free(g_mp3_decoder);
-        g_mp3_decoder = NULL;
-        g_decoder_instance = NULL;
-        g_decoder_initialized = false;
-        return MP3_DECODE_ERROR_UNKNOWN;
-    }
-    
-    printf("MP3 decoder created successfully\n");
-    printf("Thread handle: 0x%x, Queue ID: 0x%x\n", g_mp3_decoder->thread_handle, g_mp3_decoder->queue_id);
-    return MP3_DECODE_SUCCESS;
-}
-
-/**
- * @brief 销毁 MP3 解码器
- */
-void mp3_decoder_destroy(void)
-{
-    if (!g_decoder_initialized || !g_mp3_decoder) {
-        printf("MP3 decoder not initialized\n");
-        return;
-    }
-    
-    // 停止解码线程
-    if (g_mp3_decoder->thread_running) {
-        g_mp3_decoder->thread_running = false;
-        g_mp3_decoder->decode_finished = true;
-        
-        // 等待线程结束
-        int timeout = 100; // 10秒超时
-        while (g_mp3_decoder->thread_running && timeout > 0) {
-            osDelay(100); // 等待100ms
-            timeout--;
-        }
-        
-        if (g_mp3_decoder->thread_running) {
-            printf("Warning: Thread did not stop gracefully\n");
-        }
-    }
-    
-    // 删除队列
-    if (g_mp3_decoder->queue_id) {
-        osal_msg_queue_delete(g_mp3_decoder->queue_id);
-        printf("MP3 queue deleted\n");
-    }
-    
-    // 清理内存
-    if (g_mp3_decoder->mp3_buffer) {
-        free(g_mp3_decoder->mp3_buffer);
-    }
-    if (g_mp3_decoder->pcm_buffer) {
-        free(g_mp3_decoder->pcm_buffer);
-    }
-    
-    // 释放解码器结构体
-    free(g_mp3_decoder);
-    g_mp3_decoder = NULL;
-    
-    // 清理全局实例
-    g_decoder_instance = NULL;
-    g_decoder_initialized = false;
-    
-    printf("MP3 decoder destroyed\n");
-}
-
-/**
- * @brief 从文件加载 MP3 数据
- * @param filename MP3 文件路径
- * @return mp3_decode_error_t 错误码
- */
-mp3_decode_error_t mp3_decoder_load_file(const char *filename)
-{
-    if (!g_decoder_initialized || !g_mp3_decoder || !filename) {
-        return MP3_DECODE_ERROR_UNKNOWN;
-    }
-    
-    FILE *file = fopen(filename, "rb");
-    if (!file) {
-        printf("Error: Cannot open file %s: %s\n", filename, strerror(errno));
-        return MP3_DECODE_ERROR_FILE_OPEN;
-    }
-    
-    // 获取文件大小
-    fseek(file, 0, SEEK_END);
-    long file_size = ftell(file);
-    fseek(file, 0, SEEK_SET);
-    
-    if (file_size <= 0) {
-        printf("Error: Invalid file size: %ld\n", file_size);
-        fclose(file);
-        return MP3_DECODE_ERROR_FILE_READ;
-    }
-    
-    // 检查文件大小是否超过缓冲区
-    if ((size_t)file_size > g_mp3_decoder->mp3_buffer_size) {
-        printf("Error: File too large (%ld bytes), max supported: %zu bytes\n", 
-               file_size, g_mp3_decoder->mp3_buffer_size);
-        fclose(file);
-        return MP3_DECODE_ERROR_FILE_READ;
-    }
-    
-    // 读取文件数据
-    size_t bytes_read = fread(g_mp3_decoder->mp3_buffer, 1, file_size, file);
-    fclose(file);
-    
-    if (bytes_read != (size_t)file_size) {
-        printf("Error: Failed to read complete file\n");
-        return MP3_DECODE_ERROR_FILE_READ;
-    }
-    
-    g_mp3_decoder->mp3_data_size = bytes_read;
-    printf("Loaded MP3 file: %s (%zu bytes)\n", filename, bytes_read);
-    
-    return MP3_DECODE_SUCCESS;
-}
-
-/**
- * @brief 向 MP3 队列写入数据
- * @param data MP3 数据
- * @param data_size 数据大小
- * @param is_last_packet 是否为最后一个数据包
- * @return mp3_decode_error_t 错误码
- */
-mp3_decode_error_t mp3_decoder_write_data(const uint8_t *data, size_t data_size, bool is_last_packet)
-{
-    if (!g_decoder_initialized || !g_mp3_decoder || !data || data_size == 0 || !g_mp3_decoder->queue_id) {
-        return MP3_DECODE_ERROR_UNKNOWN;
-    }
-    
-    mp3_queue_item_t queue_item;
-    memset(&queue_item, 0, sizeof(queue_item));
-    
-    // 复制数据到队列项
-    size_t copy_size = (data_size > MP3_QUEUE_ITEM_SIZE) ? MP3_QUEUE_ITEM_SIZE : data_size;
-    memcpy(queue_item.data, data, copy_size);
-    queue_item.data_size = copy_size;
-    queue_item.is_last_packet = is_last_packet;
-    
-    // 写入队列
-    int ret = osal_msg_queue_write_copy(g_mp3_decoder->queue_id, &queue_item, 
-                                       sizeof(mp3_queue_item_t), 1000); // 1秒超时
-    
-    if (ret == OSAL_SUCCESS) {
-        printf("MP3 data written to queue: %zu bytes%s\n", 
-               copy_size, is_last_packet ? " (last packet)" : "");
-        return MP3_DECODE_SUCCESS;
-    } else {
-        printf("Error: Failed to write to queue: %d\n", ret);
-        return MP3_DECODE_ERROR_UNKNOWN;
-    }
-}
-
-/**
- * @brief 设置 PCM 回调函数
- * @param callback PCM 回调函数
- */
-void mp3_decoder_set_callback(void (*callback)(const int16_t *pcm_data, int samples, int channels, int sample_rate))
-{
-    if (g_decoder_initialized && g_mp3_decoder) {
-        g_mp3_decoder->pcm_callback = callback;
-        printf("PCM callback set\n");
-    }
-}
-
-/**
- * @brief 等待解码完成
- * @param timeout_ms 超时时间(毫秒),0表示无限等待
- * @return mp3_decode_error_t 错误码
- */
-mp3_decode_error_t mp3_decoder_wait_finish(uint32_t timeout_ms)
-{
-    if (!g_decoder_initialized || !g_mp3_decoder) {
-        return MP3_DECODE_ERROR_UNKNOWN;
-    }
-    
-    uint32_t start_time = osKernelGetTickCount();
-    uint32_t timeout_ticks = (timeout_ms > 0) ? (timeout_ms / osKernelGetTickFreq()) : UINT32_MAX;
-    
-    while (!g_mp3_decoder->decode_finished && g_mp3_decoder->thread_running) {
-        if (timeout_ms > 0) {
-            uint32_t current_time = osKernelGetTickCount();
-            if ((current_time - start_time) >= timeout_ticks) {
-                printf("Wait timeout\n");
-                return MP3_DECODE_ERROR_UNKNOWN;
-            }
-        }
-        osDelay(10); // 等待10ms
-    }
-    
-    if (g_mp3_decoder->decode_finished) {
-        printf("Decode finished successfully\n");
-        return MP3_DECODE_SUCCESS;
-    } else {
-        printf("Decode thread stopped unexpectedly\n");
-        return MP3_DECODE_ERROR_UNKNOWN;
-    }
-}
-
-/**
- * @brief 异步解码 MP3 数据(使用队列机制)
- * @param callback PCM 数据回调函数,参数为 (pcm_data, samples, channels, sample_rate)
- * @return mp3_decode_error_t 错误码
- * @note 此函数启动异步解码,不等待完成。解码在后台线程中进行。
- */
-mp3_decode_error_t mp3_decoder_decode(void (*callback)(const int16_t *pcm_data, int samples, int channels, int sample_rate))
-{
-    if (!g_decoder_initialized || !g_mp3_decoder || g_mp3_decoder->mp3_data_size == 0 || !g_mp3_decoder->queue_id) {
-        return MP3_DECODE_ERROR_UNKNOWN;
-    }
-    
-    // 设置回调函数
-    g_mp3_decoder->pcm_callback = callback;
-    
-    printf("Starting MP3 decoding with queue mechanism...\n");
-    
-    // 将 MP3 数据分块写入队列
-    size_t offset = 0;
-    int packet_count = 0;
-    
-    while (offset < g_mp3_decoder->mp3_data_size) {
-        size_t chunk_size = MP3_QUEUE_ITEM_SIZE;
-        if (offset + chunk_size > g_mp3_decoder->mp3_data_size) {
-            chunk_size = g_mp3_decoder->mp3_data_size - offset;
-        }
-        
-        bool is_last = (offset + chunk_size >= g_mp3_decoder->mp3_data_size);
-        
-        mp3_decode_error_t ret = mp3_decoder_write_data(g_mp3_decoder->mp3_buffer + offset, 
-                                                        chunk_size, is_last);
-        if (ret != MP3_DECODE_SUCCESS) {
-            printf("Error writing data to queue at offset %zu\n", offset);
-            return ret;
-        }
-        
-        offset += chunk_size;
-        packet_count++;
-        
-        // 小延迟,避免队列过满
-        osDelay(10);
-    }
-    
-    printf("All MP3 data written to queue: %d packets\n", packet_count);
-    
-    // 异步解码,不等待完成
-    printf("MP3 decoding started in background thread\n");
-    return MP3_DECODE_SUCCESS;
-}
-
-/**
- * @brief 获取解码统计信息
- * @param frames 输出帧数
- * @param samples 输出样本数
- * @param duration_ms 输出时长(毫秒)
- */
-void mp3_decoder_get_stats(int *frames, int *samples, int *duration_ms)
-{
-    if (g_decoder_initialized && g_mp3_decoder) {
-        if (frames) *frames = g_mp3_decoder->total_frames_decoded;
-        if (samples) *samples = g_mp3_decoder->total_samples_decoded;
-        if (duration_ms && g_mp3_decoder->frame_info.hz > 0) {
-            *duration_ms = (g_mp3_decoder->total_samples_decoded * 1000) / 
-                          (g_mp3_decoder->frame_info.hz * g_mp3_decoder->frame_info.channels);
-        }
-    }
-}
-
-/**
- * @brief 将 PCM 数据保存为 WAV 文件
- * @param filename 输出文件名
- * @param pcm_data PCM 数据
- * @param samples 样本数
- * @param channels 声道数
- * @param sample_rate 采样率
- * @return int 成功返回 0,失败返回 -1
- */
-int save_pcm_as_wav(const char *filename, const int16_t *pcm_data, int samples, int channels, int sample_rate)
-{
-    FILE *file = fopen(filename, "wb");
-    if (!file) {
-        printf("Error: Cannot create WAV file %s\n", filename);
-        return -1;
-    }
-    
-    // WAV 文件头
-    int data_size = samples * channels * sizeof(int16_t);
-    int file_size = 36 + data_size;
-    
-    // RIFF 头
-    fwrite("RIFF", 1, 4, file);
-    fwrite(&file_size, 4, 1, file);
-    fwrite("WAVE", 1, 4, file);
-    
-    // fmt 块
-    fwrite("fmt ", 1, 4, file);
-    int fmt_size = 16;
-    fwrite(&fmt_size, 4, 1, file);
-    int16_t audio_format = 1; // PCM
-    fwrite(&audio_format, 2, 1, file);
-    fwrite(&channels, 2, 1, file);
-    fwrite(&sample_rate, 4, 1, file);
-    int byte_rate = sample_rate * channels * sizeof(int16_t);
-    fwrite(&byte_rate, 4, 1, file);
-    int16_t block_align = channels * sizeof(int16_t);
-    fwrite(&block_align, 2, 1, file);
-    int16_t bits_per_sample = 16;
-    fwrite(&bits_per_sample, 2, 1, file);
-    
-    // data 块
-    fwrite("data", 1, 4, file);
-    fwrite(&data_size, 4, 1, file);
-    fwrite(pcm_data, sizeof(int16_t), samples * channels, file);
-    
-    fclose(file);
-    printf("WAV file saved: %s (%d samples)\n", filename, samples);
-    return 0;
-}
-
-// 示例回调函数:打印 PCM 数据信息
-void pcm_callback_example(const int16_t *pcm_data, int samples, int channels, int sample_rate)
-{
-    static int call_count = 0;
-    call_count++;
-    
-    if (call_count % 100 == 1) { // 每100帧打印一次
-        printf("Frame %d: %d samples, %d channels, %d Hz\n", 
-               call_count, samples, channels, sample_rate);
-    }
-}
-
-// 示例回调函数:保存 PCM 数据到文件
-void pcm_callback_save_to_file(const int16_t *pcm_data, int samples, int channels, int sample_rate)
-{
-    static FILE *pcm_file = NULL;
-    static int frame_count = 0;
-    
-    if (!pcm_file) {
-        pcm_file = fopen("output.pcm", "wb");
-        if (!pcm_file) {
-            printf("Error: Cannot create PCM file\n");
-            return;
-        }
-        printf("PCM data will be saved to output.pcm\n");
-    }
-    
-    fwrite(pcm_data, sizeof(int16_t), samples * channels, pcm_file);
-    frame_count++;
-    
-    if (frame_count % 100 == 0) {
-        printf("Saved %d frames to PCM file\n", frame_count);
-    }
-}
-
-
-/**
- * @brief 初始化 MP3 解码器(简化接口)
- * @param callback PCM 数据回调函数,可为 NULL
- * @return mp3_decode_error_t 错误码
- */
-mp3_decode_error_t mp3_decoder_init_decode(void (*callback)(const int16_t *pcm_data, int samples, int channels, int sample_rate))
-{
-    mp3_decoder_create();
-
-    if (!g_decoder_initialized || !g_mp3_decoder) {
-        printf("Error: Decoder not initialized\n");
-        return MP3_DECODE_ERROR_UNKNOWN;
-    }
-    
-    if (!g_mp3_decoder->queue_id) {
-        printf("Error: Decoder not properly initialized\n");
-        return MP3_DECODE_ERROR_UNKNOWN;
-    }
-    
-    // 设置回调函数
-    g_mp3_decoder->pcm_callback = callback;
-    
-    // 重置解码状态
-    g_mp3_decoder->decode_finished = false;
-    g_mp3_decoder->total_samples_decoded = 0;
-    g_mp3_decoder->total_frames_decoded = 0;
-    
-    printf("MP3 decoder initialized for decoding\n");
-    
-    return MP3_DECODE_SUCCESS;
-}
-
-/**
- * @brief 写入 MP3 数据(简化接口)
- * @param data MP3 数据
- * @param data_size 数据大小
- * @param is_last 是否为最后一块数据
- * @return mp3_decode_error_t 错误码
- */
-mp3_decode_error_t mp3_decoder_write(const uint8_t *data, size_t data_size, bool is_last)
-{
-    if (!g_decoder_initialized || !g_mp3_decoder || !data || data_size == 0) {
-        printf("Error: Invalid parameters\n");
-        return MP3_DECODE_ERROR_UNKNOWN;
-    }
-    
-    if (!g_mp3_decoder->queue_id) {
-        printf("Error: Decoder not initialized\n");
-        return MP3_DECODE_ERROR_UNKNOWN;
-    }
-    
-    // 调用现有的写数据函数
-    return mp3_decoder_write_data(data, data_size, is_last);
-}
-
-/**
- * @brief 检查解码是否完成(简化接口)
- * @return bool true 表示解码完成,false 表示还在进行中
- */
-bool mp3_decoder_is_finished(void)
-{
-    if (!g_decoder_initialized || !g_mp3_decoder) {
-        return true;
-    }
-    return g_mp3_decoder->decode_finished;
-}
-
-/**
- * @brief 获取解码进度(简化接口)
- * @param frames 输出已解码帧数
- * @param samples 输出已解码样本数
- * @return mp3_decode_error_t 错误码
- */
-mp3_decode_error_t mp3_decoder_get_progress(int *frames, int *samples)
-{
-    if (!g_decoder_initialized || !g_mp3_decoder) {
-        return MP3_DECODE_ERROR_UNKNOWN;
-    }
-    
-    if (frames) {
-        *frames = g_mp3_decoder->total_frames_decoded;
-    }
-    if (samples) {
-        *samples = g_mp3_decoder->total_samples_decoded;
-    }
-    
-    return MP3_DECODE_SUCCESS;
-}
-
-/**
- * @brief 停止解码(简化接口)
- * @return mp3_decode_error_t 错误码
- */
-mp3_decode_error_t mp3_decoder_stop(void)
-{
-    if (!g_decoder_initialized || !g_mp3_decoder) {
-        return MP3_DECODE_ERROR_UNKNOWN;
-    }
-    
-    g_mp3_decoder->thread_running = false;
-    g_mp3_decoder->decode_finished = true;
-    
-    printf("MP3 decoder stopped\n");
-    return MP3_DECODE_SUCCESS;
-}