实现mp3的顺序播放功能
authorcheng <chengyf@zhihuishixun.com>
Sat, 13 Sep 2025 08:32:51 +0000 (16:32 +0800)
committercheng <chengyf@zhihuishixun.com>
Sat, 13 Sep 2025 08:32:51 +0000 (16:32 +0800)
code/sdk/application/wearable/fd230/nativeapp/CMakeLists.txt
code/sdk/application/wearable/fd230/nativeapp/nativeui/leiniao/include/ln_story_player_simple.h [new file with mode: 0644]
code/sdk/application/wearable/fd230/nativeapp/nativeui/leiniao/src/ln_story_player_simple.cpp [new file with mode: 0644]
code/sdk/zhihui/leiniao/imsl_ln/leiniao_api_at.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api.h

index 76dac27..172bcd8 100644 (file)
@@ -76,6 +76,7 @@ set(PRIVATE_HEADER
     ${ROOT_DIR}/zhihui/4g/cat1Service/include
     ${ROOT_DIR}/zhihui/4g/cat1Service/Service
     ${ROOT_DIR}/zhihui/ai/include/
+    ${ROOT_DIR}/zhihui/leiniao/imsl_ln/ #暂时添加
 
 )
 
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/leiniao/include/ln_story_player_simple.h b/code/sdk/application/wearable/fd230/nativeapp/nativeui/leiniao/include/ln_story_player_simple.h
new file mode 100644 (file)
index 0000000..b558268
--- /dev/null
@@ -0,0 +1,93 @@
+#ifndef LN_STORY_PLAYER_SIMPLE_H
+#define LN_STORY_PLAYER_SIMPLE_H
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C"
+{
+#endif
+#endif
+
+#include "stdint.h"
+#include "errcode.h"
+#include <stdbool.h>
+
+/* ============================== 精简版故事播放器接口 ============================== */
+
+/**
+ * @brief 开始播放指定目录下的音频文件(顺序播放)
+ * @param story_dir 故事文件目录路径
+ * @return true 成功,false 失败
+ * 
+ * 功能说明:
+ * - 自动扫描目录下的音频文件(支持mp3, wav, aac, m4a格式)
+ * - 按文件名顺序播放所有音频文件
+ * - 播放完一个文件后自动播放下一个文件
+ * - 音频系统只初始化一次,提高效率
+ */
+bool ln_story_player_simple_start(const char *story_dir);
+
+/**
+ * @brief 停止播放并清理所有资源
+ * @return 无返回值
+ * 
+ * 功能说明:
+ * - 立即停止当前播放
+ * - 清理所有音频资源
+ * - 重置播放器状态
+ */
+void ln_story_player_simple_stop(void);
+
+/**
+ * @brief 检查是否正在播放
+ * @return true 正在播放,false 未播放
+ */
+bool ln_story_player_simple_is_playing(void);
+
+/**
+ * @brief 获取当前播放的文件索引
+ * @return 当前播放的文件索引(从0开始),-1表示未播放
+ */
+int ln_story_player_simple_get_current_index(void);
+
+/**
+ * @brief 获取总文件数
+ * @return 扫描到的音频文件总数
+ */
+int ln_story_player_simple_get_file_count(void);
+
+/**
+ * @brief 处理播放器消息队列命令(内部使用)
+ * @param cmd 命令类型
+ * @param data 数据指针
+ * @param len 数据长度
+ */
+void ln_story_player_simple_handle_message(int cmd, char *data, uint32_t len);
+
+/* ============================== 使用示例 ============================== */
+
+/*
+// 开始播放
+if (ln_story_player_simple_start("/user/leiniao/story/choose"))
+{
+    printf("播放开始成功\n");
+}
+
+// 检查播放状态
+if (ln_story_player_simple_is_playing())
+{
+    printf("正在播放第 %d 个文件,共 %d 个文件\n", 
+           ln_story_player_simple_get_current_index(),
+           ln_story_player_simple_get_file_count());
+}
+
+// 停止播放
+ln_story_player_simple_stop();
+*/
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif
+#endif
+#endif
diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/leiniao/src/ln_story_player_simple.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/leiniao/src/ln_story_player_simple.cpp
new file mode 100644 (file)
index 0000000..4a0a22f
--- /dev/null
@@ -0,0 +1,550 @@
+#include "leiniao/include/ln_story_player_simple.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <service/graphic_service.h>
+/* 引入播放器相关头文件 */
+#include "player.h"
+#include "source.h"
+#include "audio_manager.h"
+#include <audio_base_type.h>
+#include <audio_player_service.h>
+#include <custom_utils.h>
+#include <los_base.h>
+/* 引入消息队列相关头文件 */
+#include "ln_api.h"
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+using OHOS::Media::Player;
+using OHOS::Media::Source;
+using namespace OHOS::Media;
+using namespace::Audio;
+using Audio::AudioManager;
+
+/* ============================== 精简版播放器结构体 ============================== */
+
+/* 播放器消息命令定义 */
+
+
+typedef struct
+{
+    /* 文件列表 */
+    char file_paths[100][256];  // 最多100个文件
+    int file_count;
+    int current_index;
+    
+    /* 播放器对象 */
+    std::shared_ptr<Player> player;
+    std::shared_ptr<Source> source;
+    std::map<std::string, std::string> header;
+    
+    /* 音频管理 */
+    AudioSession sessionId;
+    std::shared_ptr<AudioInterrupt> interrupt;
+    std::shared_ptr<InterruptListener> interruptListener;
+    bool audio_initialized;
+    bool is_activate_audio_interrupt;  // 音频中断激活标志
+    
+    /* 状态标志 */
+    bool is_playing;
+    bool should_stop;
+} ln_story_player_simple_t;
+
+
+static bool ln_story_player_simple_play_file(ln_story_player_simple_t *impl, int file_index);
+
+/* ============================== 播放器回调类 ============================== */
+
+class SimplePlayerCallback : public PlayerCallback
+{
+public:
+    SimplePlayerCallback(ln_story_player_simple_t *impl) : impl_(impl) {}
+
+    void OnPlaybackComplete() override
+    {
+        if (impl_ && !impl_->should_stop)
+        {
+            printf("[SIMPLE_PLAYER] playback completed, scheduling next file\n");
+            // 通过消息队列播放下一个文件,避免在回调中直接调用
+            impl_->current_index++;
+            if (impl_->current_index < impl_->file_count)
+            {
+                // 发送消息到 ln_http_server_send_task 线程
+                do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_AUDIO_PLAY_NEXT, NULL, 0);
+            }
+            else
+            {
+                printf("[SIMPLE_PLAYER] all files played, stopping(所有文件播放完成)\n");
+                impl_->is_playing = false;
+            }
+        }
+    }
+
+    void OnError(int32_t errorType, int32_t errorCode) override
+    {
+        printf("[SIMPLE_PLAYER] playback error: type=%d, code=%d\n", errorType, errorCode);
+        if (impl_)
+        {
+            impl_->is_playing = false;
+        }
+    }
+
+    void OnInfo(int32_t type, int32_t extra) override {}
+    void OnRewindToComplete() override {}
+
+private:
+    ln_story_player_simple_t *impl_;
+};
+
+/* ============================== 中断监听器类 ============================== */
+
+class SimplePlayerInterruptListener : public InterruptListener
+{
+public:
+    SimplePlayerInterruptListener(ln_story_player_simple_t *impl) : impl_(impl) {}
+
+    void OnInterrupt(int32_t type, int32_t hint) override
+    {
+        if (!impl_)
+            return;
+
+        printf("[SIMPLE_PLAYER] OnInterrupt: type=%d, hint=%d\n", type, hint);
+
+        if (type == INTERRUPT_TYPE_BEGIN && hint == INTERRUPT_HINT_PAUSE)
+        {
+            if (impl_->is_playing && impl_->player)
+            {
+                impl_->player->Pause();
+            }
+        }
+        else if (type == INTERRUPT_TYPE_END && hint == INTERRUPT_HINT_RESUME)
+        {
+            if (impl_->is_playing && impl_->player)
+            {
+                impl_->player->Play();
+            }
+        }
+        else if (type == INTERRUPT_TYPE_BEGIN && hint == INTERRUPT_HINT_STOP)
+        {
+            if (impl_->is_playing)
+            {
+                impl_->player->Stop();
+                impl_->is_playing = false;
+            }
+        }
+    }
+
+private:
+    ln_story_player_simple_t *impl_;
+};
+
+/* ============================== 全局变量 ============================== */
+
+static ln_story_player_simple_t g_simple_player = {0};
+static std::shared_ptr<SimplePlayerCallback> g_player_callback = nullptr;
+static std::shared_ptr<SimplePlayerInterruptListener> g_interrupt_listener = nullptr;
+
+/* ============================== 内部函数 ============================== */
+
+// 处理播放器消息队列命令
+void ln_story_player_simple_handle_message(int cmd, char *data, uint32_t len)
+{
+    if (cmd == LN_HTTP_SERVICE_CMD_AUDIO_PLAY_NEXT)
+    {
+        printf("[SIMPLE_PLAYER] received play next command\n");
+        if (g_simple_player.is_playing && !g_simple_player.should_stop)
+        {
+            if (g_simple_player.current_index < g_simple_player.file_count)
+            {
+                ln_story_player_simple_play_file(&g_simple_player, g_simple_player.current_index);
+            }
+        }
+    }
+}
+
+// 音频初始化(只初始化一次)
+static bool AudioInit(void)
+{
+    if (g_simple_player.audio_initialized)
+    {
+        return true;
+    }
+    
+    AudioManager& amInstance = AudioManager::GetInstance();
+    if (!amInstance.Initialize())
+    {
+        printf("[SIMPLE_PLAYER] AudioManager Initialize failed\n");
+        return false;
+    }
+    
+    g_simple_player.player = std::make_shared<Player>();
+    if (!g_simple_player.player)
+    {
+        printf("[SIMPLE_PLAYER] Player create failed\n");
+        return false;
+    }
+    
+    g_player_callback = std::make_shared<SimplePlayerCallback>(&g_simple_player);
+    if (!g_player_callback)
+    {
+        printf("[SIMPLE_PLAYER] PlayerCallback create failed\n");
+        return false;
+    }
+    
+    g_simple_player.audio_initialized = true;
+    g_simple_player.is_activate_audio_interrupt = false;
+    
+    printf("[SIMPLE_PLAYER] Audio initialized successfully\n");
+    return true;
+}
+
+// 比较函数,用于文件名排序
+static int CompareFileNames(const void *a, const void *b)
+{
+    const char *file_a = (const char *)a;
+    const char *file_b = (const char *)b;
+    
+    // 提取文件名(去掉路径)
+    const char *name_a = strrchr(file_a, '/');
+    const char *name_b = strrchr(file_b, '/');
+    
+    if (name_a) name_a++;
+    else name_a = file_a;
+    if (name_b) name_b++;
+    else name_b = file_b;
+    
+    // 提取数字部分进行比较
+    int num_a = atoi(name_a);
+    int num_b = atoi(name_b);
+    
+    return num_a - num_b;
+}
+
+// 扫描目录获取音频文件
+static bool ScanAudioFiles(const char *story_dir)
+{
+    printf("[SIMPLE_PLAYER] scanning directory: %s\n", story_dir);
+    
+    // 检查目录是否存在
+    if (access(story_dir, F_OK) == -1)
+    {
+        printf("[SIMPLE_PLAYER] directory does not exist: %s\n", story_dir);
+        return false;
+    }
+    
+    DIR *dir = opendir(story_dir);
+    if (!dir)
+    {
+        printf("[SIMPLE_PLAYER] failed to open directory: %s\n", story_dir);
+        return false;
+    }
+    
+    g_simple_player.file_count = 0;
+    struct dirent *entry;
+    
+    while ((entry = readdir(dir)) != NULL && g_simple_player.file_count < 100)
+    {
+        // 跳过 . 和 .. 目录
+        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
+        {
+            continue;
+        }
+        
+        // 检查是否是音频文件
+        const char *ext = strrchr(entry->d_name, '.');
+        if (ext && (strcasecmp(ext, ".mp3") == 0 || strcasecmp(ext, ".wav") == 0 || 
+                    strcasecmp(ext, ".aac") == 0 || strcasecmp(ext, ".m4a") == 0))
+        {
+            snprintf(g_simple_player.file_paths[g_simple_player.file_count], 
+                    sizeof(g_simple_player.file_paths[g_simple_player.file_count]),
+                    "%s/%s", story_dir, entry->d_name);
+            g_simple_player.file_count++;
+            printf("[SIMPLE_PLAYER] found audio file: %s\n", entry->d_name);
+        }
+    }
+    
+    closedir(dir);
+    
+    // 对文件路径进行排序(按文件名中的数字排序)
+    if (g_simple_player.file_count > 1)
+    {
+        qsort(g_simple_player.file_paths, g_simple_player.file_count, 
+              sizeof(g_simple_player.file_paths[0]), CompareFileNames);
+        printf("[SIMPLE_PLAYER] files sorted by name\n");
+    }
+    
+    printf("[SIMPLE_PLAYER] found %d audio files\n", g_simple_player.file_count);
+    return g_simple_player.file_count > 0;
+}
+
+// 播放指定文件
+static bool ln_story_player_simple_play_file(ln_story_player_simple_t *impl, int file_index)
+{
+    if (file_index < 0 || file_index >= impl->file_count)
+    {
+        printf("[SIMPLE_PLAYER] invalid file index: %d\n", file_index);
+        return false;
+    }
+    
+    printf("[SIMPLE_PLAYER] playing file %d: %s\n", file_index, impl->file_paths[file_index]);
+    
+    // 检查播放器状态
+    int32_t player_state;
+    impl->player->GetPlayerState(player_state);
+    printf("[SIMPLE_PLAYER] current player state: %d\n", player_state);
+    
+    // 根据播放器状态进行处理
+    if (player_state == PLAYER_STARTED || player_state == PLAYER_PLAYBACK_COMPLETE)
+    {
+        printf("[SIMPLE_PLAYER] stopping and resetting player\n");
+        impl->player->Stop();
+        impl->player->Reset();
+    }
+    else if (player_state == PLAYER_PAUSED || player_state == PLAYER_IDLE)
+    {
+        printf("[SIMPLE_PLAYER] resetting player\n");
+        impl->player->Reset();
+    }
+    else
+    {
+        printf("[SIMPLE_PLAYER] unknown state, forcing reset\n");
+        impl->player->Stop();
+        impl->player->Reset();
+    }
+    
+    // 设置回调函数
+    g_simple_player.player->SetPlayerCallback(g_player_callback);
+
+    // 设置音频源
+    impl->source = std::make_shared<Source>(impl->file_paths[file_index], impl->header);
+    if (!impl->source)
+    {
+        printf("[SIMPLE_PLAYER] failed to create source\n");
+        return false;
+    }
+    
+    int32_t ret = impl->player->SetSource(*impl->source);
+    if (ret != 0)
+    {
+        printf("[SIMPLE_PLAYER] set source failed: %d\n", ret);
+        impl->player->Reset();
+        return false;
+    }
+    
+    // 配置播放器
+    impl->player->SetAudioStreamType(AUDIO_STREAM_MUSIC);
+    
+    // 准备播放
+    ret = impl->player->Prepare();
+    if (ret != 0)
+    {
+        printf("[SIMPLE_PLAYER] prepare failed: %d\n", ret);
+        impl->player->Reset();
+        return false;
+    }
+    
+    // 设置音频会话和中断监听
+    AudioManager &amInstance = AudioManager::GetInstance();
+    
+    // 如果已经激活过音频中断,先清理
+    if (impl->is_activate_audio_interrupt && impl->interrupt && impl->sessionId != AUDIO_SESSION_ID_NONE)
+    {
+        amInstance.DeactivateAudioInterrupt(*impl->interrupt);
+        impl->is_activate_audio_interrupt = false;
+        printf("[SIMPLE_PLAYER] previous audio interrupt deactivated\n");
+    }
+    
+    // 创建新的会话和中断
+    impl->sessionId = amInstance.MakeSessionId();
+    if (impl->sessionId == AUDIO_SESSION_ID_NONE)
+    {
+        printf("[SIMPLE_PLAYER] failed to create session\n");
+        impl->player->Reset();
+        return false;
+    }
+    
+    impl->interruptListener = std::make_shared<SimplePlayerInterruptListener>(impl);
+    impl->interrupt = std::make_shared<AudioInterrupt>();
+    impl->interrupt->streamType = AUDIO_STREAM_MUSIC;
+    impl->interrupt->sessionID = impl->sessionId;
+    impl->interrupt->interruptListener = impl->interruptListener;
+    
+    if (amInstance.ActivateAudioInterrupt(*impl->interrupt) == INTERRUPT_FAILED)
+    {
+        printf("[SIMPLE_PLAYER] failed to activate audio interrupt\n");
+        impl->player->Reset();
+        return false;
+    }
+    
+    impl->is_activate_audio_interrupt = true;
+    printf("[SIMPLE_PLAYER] audio interrupt activated for file %d\n", file_index);
+    
+    // 设置音频会话ID
+    ret = impl->player->SetAudioSessionId(impl->sessionId);
+    if (ret != 0)
+    {
+        printf("[SIMPLE_PLAYER] SetAudioSessionId failed: %d\n", ret);
+        impl->player->Reset();
+        amInstance.DeactivateAudioInterrupt(*impl->interrupt);
+        return false;
+    }
+    
+    // 开始播放
+    ret = impl->player->Play();
+    if (ret != 0)
+    {
+        printf("[SIMPLE_PLAYER] play failed: %d\n", ret);
+        impl->player->Stop();
+        impl->player->Reset();
+        amInstance.DeactivateAudioInterrupt(*impl->interrupt);
+        return false;
+    }
+    
+    impl->is_playing = true;
+    printf("[SIMPLE_PLAYER] started playing file %d\n", file_index);
+    return true;
+}
+
+// 清理音频中断
+static void ClearActivateAudioInterrupt(void)
+{
+    if (g_simple_player.is_activate_audio_interrupt && g_simple_player.interrupt && g_simple_player.sessionId != AUDIO_SESSION_ID_NONE)
+    {
+        AudioManager &amInstance = AudioManager::GetInstance();
+        amInstance.DeactivateAudioInterrupt(*g_simple_player.interrupt);
+        g_simple_player.is_activate_audio_interrupt = false;
+        printf("[SIMPLE_PLAYER] audio interrupt cleared\n");
+    }
+}
+
+// 清理资源
+static void CleanupResources(void)
+{
+    if (g_simple_player.player)
+    {
+        g_simple_player.player->Stop();
+        g_simple_player.player->Reset();
+        g_simple_player.player->Release();
+        g_simple_player.player.reset();
+    }
+    
+    if (g_simple_player.is_activate_audio_interrupt && g_simple_player.interrupt && g_simple_player.sessionId != AUDIO_SESSION_ID_NONE)
+    {
+        AudioManager &amInstance = AudioManager::GetInstance();
+        amInstance.DeactivateAudioInterrupt(*g_simple_player.interrupt);
+        g_simple_player.is_activate_audio_interrupt = false;
+        printf("[SIMPLE_PLAYER] audio interrupt deactivated\n");
+    }
+    
+    g_simple_player.interrupt.reset();
+    g_simple_player.interruptListener.reset();
+    g_player_callback.reset();
+    
+    memset(&g_simple_player, 0, sizeof(g_simple_player));
+    printf("[SIMPLE_PLAYER] resources cleaned up\n");
+}
+
+/* ============================== 公共接口 ============================== */
+
+// 开始播放指定目录下的音频文件(顺序播放)
+bool ln_story_player_simple_start(const char *story_dir)
+{
+    if (!story_dir)
+    {
+        printf("[SIMPLE_PLAYER] invalid story directory\n");
+        return false;
+    }
+    
+    printf("[SIMPLE_PLAYER] starting playback: %s\n", story_dir);
+    
+    // 如果已经在播放,先停止
+    if (g_simple_player.is_playing)
+    {
+        printf("[SIMPLE_PLAYER] stopping playback\n");
+        ln_story_player_simple_stop();
+    }
+    
+    // 初始化音频(只初始化一次)
+    if (!AudioInit())
+    {
+        printf("[SIMPLE_PLAYER] audio init failed\n");
+        return false;
+    }
+    
+    // 扫描音频文件
+    if (!ScanAudioFiles(story_dir))
+    {
+        printf("[SIMPLE_PLAYER] no audio files found\n");
+        return false;
+    }
+    
+    // 开始播放第一个文件
+    g_simple_player.current_index = 0;
+    g_simple_player.should_stop = false;
+    
+    if (!ln_story_player_simple_play_file(&g_simple_player, 0))
+    {
+        printf("[SIMPLE_PLAYER] failed to start playing\n");
+        return false;
+    }
+    
+    printf("[SIMPLE_PLAYER] playback started successfully\n");
+    return true;
+}
+
+// 停止播放并清理资源
+void ln_story_player_simple_stop(void)
+{
+    printf("[SIMPLE_PLAYER] stopping playback\n");
+    
+    g_simple_player.should_stop = true;
+    g_simple_player.is_playing = false;
+    
+    if (g_simple_player.player)
+    {
+        g_simple_player.player->Stop();
+        g_simple_player.player->Reset();
+    }
+    
+    // 清理音频中断
+    ClearActivateAudioInterrupt();
+    
+    // 清理资源
+    CleanupResources();
+    
+    printf("[SIMPLE_PLAYER] playback stopped\n");
+}
+
+// 检查是否正在播放
+bool ln_story_player_simple_is_playing(void)
+{
+    return g_simple_player.is_playing;
+}
+
+// 获取当前播放的文件索引
+int ln_story_player_simple_get_current_index(void)
+{
+    return g_simple_player.current_index;
+}
+
+// 获取总文件数
+int ln_story_player_simple_get_file_count(void)
+{
+    return g_simple_player.file_count;
+}
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __cplusplus */
index 5564caa..0ca99aa 100644 (file)
@@ -636,6 +636,24 @@ static uint32_t ln_ota_download_at(uint8_t *para, uint32_t paraLen, char *strCom
     return ERRCODE_SUCC;
 }
 
+/* 音频播放: 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);
+    printf("[LN][AUDIO_PLAY]\n");
+    do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_AUDIO_PLAY, NULL, 0);
+    return ERRCODE_SUCC;
+}
+
+/* 音频停止: AT^LN_STORY_PLAY_PREV */
+static uint32_t ln_audio_stop_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    VAD_UNUSED(para); VAD_UNUSED(paraLen); VAD_UNUSED(strCommonCmd);
+    printf("[LN][AUDIO_STOP]\n");
+    do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_AUDIO_STOP, NULL, 0);
+    return ERRCODE_SUCC;
+}
+
 
 static const at_cmd_table_t g_ln_api_cmd[] = {
     /* ----------------------------------- VAD ---------------------------------- */
@@ -660,6 +678,9 @@ static const at_cmd_table_t g_ln_api_cmd[] = {
     {(char *)"AT^LN_DEV_USERID", ln_device_get_userid_at, (char *)"获取设备绑定用户ID(JSON,GET): AT^LN_DEV_USERID"},
     {(char *)"AT^LN_OTA_GET_TASK", ln_ota_get_task_at, (char *)"获取OTA升级任务(JSON,GET): AT^LN_OTA_GET_TASK"},
     {(char *)"AT^LN_OTA_DOWNLOAD", ln_ota_download_at, (char *)"下载OTA升级包(JSON,GET->下载): AT^LN_OTA_DOWNLOAD"},
+    /* 音频播放 */
+    {(char *)"AT^LN_STORY_PLAY_AT", ln_audio_play_at, (char *)"播放音频: AT^LN_STORY_PLAY_AT"},
+    {(char *)"AT^LN_STORY_STOP_AT", ln_audio_stop_at, (char *)"停止播放音频: AT^LN_STORY_STOP_AT"},
 };
 
 at_cmd_table_t *get_leiniao_api_cmd_table(uint32_t *cmd_count)
index 971e3ac..0f1d47a 100644 (file)
 #include <soc_osal.h>
 #include "stdio.h"
 
+#ifdef BOARD_N9
+#include "leiniao/include/ln_story_player_simple.h"
+#endif
+
+// 声明播放器消息处理函数
+extern void ln_story_player_simple_handle_message(int cmd, char *data, uint32_t len);
+
 static app_task_definition_t ln_send_taskcfg = {0};
 static app_task_definition_t ln_recv_taskcfg = {0};
-#define LN_HTTP_BOARD_TASK_SIZE 0x4000
+#define LN_HTTP_BOARD_TASK_SIZE 0x3000
 
 static unsigned long g_ln_send_queue = 0xFFFFFFFF;
 static unsigned long g_ln_recv_queue = 0xFFFFFFFF;
@@ -441,6 +448,30 @@ static void ln_http_server_send_task(void const *arg)
             printf("LN_HTTP_SERVICE_CMD_OTA_DOWNLOAD:下载OTA升级包\n");
             ln_ota_download_flow_http(uapi_ln_get_device_id(), uapi_ln_get_device_token(), uapi_ln_get_device_version());
         }
+        else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_PLAY)
+        {
+            printf("LN_HTTP_SERVICE_CMD_AUDIO_PLAY:播放音频\n");
+            #ifdef BOARD_N9
+            if (ln_story_player_simple_start("/user/leiniao/story/choose"))
+            {
+                printf("播放开始成功\n");
+            }
+            #endif
+        }
+        else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_STOP)
+        {
+            printf("LN_HTTP_SERVICE_CMD_AUDIO_STOP:停止播放音频\n");
+            #ifdef BOARD_N9
+            ln_story_player_simple_stop();
+            #endif
+        }
+        else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_PLAY_NEXT)
+        {
+            printf("LN_HTTP_SERVICE_CMD_AUDIO_PLAY_NEXT:播放下一首\n");
+            #ifdef BOARD_N9
+            ln_story_player_simple_handle_message(LN_HTTP_SERVICE_CMD_AUDIO_PLAY_NEXT, NULL, 0);
+            #endif
+        }
         if (mail.data != NULL)
         {
             printf("free(mail.data)\n");
@@ -893,19 +924,19 @@ uint32_t ln_parse_and_process_json_data(const char *json_data, int interface_typ
         {
             case LN_HTTP_SERVICE_CMD_SSE_RECV:
                 snprintf(save_dir, sizeof(save_dir), "%s/speech", LN_DATA_PATH);
-                snprintf(filename, sizeof(filename), "speech_audio_%d.mp3", segment_index);
+                snprintf(filename, sizeof(filename), "%d.mp3", segment_index);
                 break;
             case LN_HTTP_RECV_STORY_CHOOSE:
                 snprintf(save_dir, sizeof(save_dir), "%s/story/choose", LN_DATA_PATH);
-                snprintf(filename, sizeof(filename), "story_choose_%d.mp3", segment_index);
+                snprintf(filename, sizeof(filename), "%d.mp3", segment_index);
                 break;
             case LN_HTTP_RECV_STORY_RANDOM:
                 snprintf(save_dir, sizeof(save_dir), "%s/story/random", LN_DATA_PATH);
-                snprintf(filename, sizeof(filename), "story_random_%d.mp3", segment_index);
+                snprintf(filename, sizeof(filename), "%d.mp3", segment_index);
                 break;
             default:
                 snprintf(save_dir, sizeof(save_dir), "%s", LN_DATA_PATH);
-                snprintf(filename, sizeof(filename), "audio_%d.mp3", segment_index);
+                snprintf(filename, sizeof(filename), "%d.mp3", segment_index);
                 break;
         }
 
index 5b927b0..982a3c3 100644 (file)
@@ -46,7 +46,7 @@ typedef struct
 /* OTA 包保存目录(最终文件仍将放在 /user/ 下)*/
 #define LN_OTA_SAVE_DIR "/user"
 /* 用户数据保存目录 */
-#define LN_DATA_PATH          "/user/leiniao/"
+#define LN_DATA_PATH          "/user/leiniao"
 
 #define LN_CLIENT_PREPARE_SIZE 2*1024
 
@@ -77,6 +77,11 @@ typedef struct
 #define LN_HTTP_SERVICE_CMD_OTA_DOWNLOAD 0x51 /* 触发 OTA 包下载(根据 getTask 的返回 packageUrl)*/
 #define LN_HTTP_RECV_OTA_DOWNLOAD_MSG 0x61   /* 可选:OTA 下载控制消息(JSON)接收)*/
 
+/* 音频播放相关命令 */
+#define LN_HTTP_SERVICE_CMD_AUDIO_PLAY 0x1001 /* 播放音频 */
+#define LN_HTTP_SERVICE_CMD_AUDIO_STOP 0x1002 /* 停止播放音频 */
+#define LN_HTTP_SERVICE_CMD_AUDIO_PLAY_NEXT 0x1003 /* 播放下一首 */
+
 typedef struct
 {
     char device_id[64];