增加雷鸟tts不同比特率适配
authorhcj <1224667075@qq.com>
Mon, 5 Jan 2026 10:17:58 +0000 (18:17 +0800)
committerhcj <1224667075@qq.com>
Mon, 5 Jan 2026 10:17:58 +0000 (18:17 +0800)
code/sdk/zhihui/ai/media/ai_media_service_ext.cpp
code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c

index 8badf51..8eae31e 100644 (file)
@@ -283,6 +283,9 @@ typedef struct {
 static MediaMutexHandle g_aiVoiceContextMutex = nullptr;
 static AIVoiceContext *g_aiVoiceContext = nullptr;
 
+// TTS音频比特率(默认64000 bps)
+static uint32_t g_tts_audio_bit_rate = 64000;
+
 // 文件缓存相关变量
 static const char* AI_AUDIO_CACHE_FILE = "/user/ai_audio_cache.dat";
 static const size_t AI_AUDIO_CHUNK_SIZE = 1024;  // 固定音频块大小:1KB
@@ -2120,11 +2123,26 @@ int32_t AI_WriteAudioData(const uint8_t* data, uint32_t size)
 
     // 写入音频数据到输出流
     int32_t ret = AudioStreamOutStreamWrite(data, size);
-    usleep(40*size);
+    
+    // 根据比特率动态计算延迟时间
+    // 延迟时间(微秒)= (数据大小(字节)* 8 * 1000000) / 比特率(bps)
+    // 当比特率更高时,延迟时间更短
+    // 公式:延迟(微秒)= (size * 8 * 1000000) / bit_rate
+    uint32_t delay_us = (uint32_t)((uint64_t)(size-(size/10*3)) * 8 * 1000000ULL / g_tts_audio_bit_rate);
+    printf("delay_us %d\r\n",delay_us);
+    
+    // 确保延迟时间在合理范围内(最小10微秒,最大100毫秒)
+    if (delay_us < 20) {
+        delay_us = 20;
+    } else if (delay_us > 100000) {
+        delay_us = 100000;
+    }
+    
+    usleep(delay_us-20);
     if (ret != 0) {
         retry_count++;
         // MEDIA_ERR_LOG("Failed to write audio data to output stream, ret=%d, retry %d/%d", ret, retry_count, MAX_RETRY_COUNT);
-        usleep(1000*300);
+        usleep(delay_us*5);
         goto AI_WRITE;
     }
     
@@ -2137,6 +2155,36 @@ int32_t AI_WriteAudioData(const uint8_t* data, uint32_t size)
     return 0;
 }
 
+/**
+ * @brief 设置TTS音频比特率
+ * @param bit_rate 比特率(bps),如果为0则使用默认值64000
+ * @return 0 成功,-1 失败(bit_rate无效)
+ */
+int32_t AI_SetTTSAudioBitRate(uint32_t bit_rate)
+{
+    if (bit_rate == 0) {
+        // 使用默认值
+        g_tts_audio_bit_rate = 64000;
+    } else if (bit_rate > 0 && bit_rate <= 10000000) {  // 限制范围:1 bps 到 10 Mbps
+        g_tts_audio_bit_rate = bit_rate;
+    } else {
+        MEDIA_ERR_LOG("Invalid bit rate: %u, must be between 1 and 1000000", bit_rate);
+        return -1;
+    }
+    
+    MEDIA_INFO_LOG("TTS audio bit rate set to: %u bps", g_tts_audio_bit_rate);
+    return 0;
+}
+
+/**
+ * @brief 获取TTS音频比特率
+ * @return 当前比特率(bps)
+ */
+uint32_t AI_GetTTSAudioBitRate(void)
+{
+    return g_tts_audio_bit_rate;
+}
+
 /**
  * @brief 检查AI语音服务是否正在运行
  * @return true 正在运行,false 未运行
index 925c6e3..9895dee 100644 (file)
@@ -7,6 +7,7 @@
 #include <stdarg.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <ctype.h>
 #include "cJSON.h" // Added for JSON parsing
 #include "custom_utils.h"
 #include "soc_osal.h"
@@ -118,6 +119,9 @@ int ln_ws_send_ping(void);
 static ws_command_t ln_ws_parse_command_string(const char *command_str);
 static int ln_ws_parse_data_message(const char *json_str, ws_data_msg_t *data_msg);
 static void ln_ws_free_data_message(ws_data_msg_t *data_msg);
+static int ln_ws_save_tts_audio_setting_to_file(void);
+static int ln_ws_load_tts_audio_setting_from_file(void);
+static uint32_t ln_ws_convert_audio_format_string_to_enum(const char *format_str);
 static void ln_ws_default_message_handler(const ws_data_msg_t *data_msg, ws_command_t type);
 static void ln_ws_process_message_with_callback(const ws_data_msg_t *data_msg);
 static int ln_ws_close_with_status(int client_fd);
@@ -171,6 +175,18 @@ typedef struct {
 // 全局音频聊天上下文
 static audio_chat_context_t g_audio_chat_ctx = {0};
 
+// TTS音频设置结构体
+typedef struct {
+    uint32_t audio_bit_rate;      // 音频比特率
+    uint32_t audio_sample_rate;    // 音频采样率
+    uint32_t audio_channel;        // 音频声道数
+    char audio_format[16];          // 音频格式(如 "mp3", "pcm")
+    bool valid;                    // 数据是否有效
+} tts_audio_setting_t;
+
+// 全局TTS音频设置
+static tts_audio_setting_t g_tts_audio_setting = {0};
+
 // 音频聊天结束标志
 static bool g_audio_chat_ending = false;
 
@@ -1320,11 +1336,40 @@ void ln_ws_audio_init(bool is_vad_server_enabled)
         .sampleRate = 16000
     };
     
+    // 从文件加载TTS音频设置
+    ln_ws_load_tts_audio_setting_from_file();
+    
+    // 初始化audioParams_out,使用默认值
     AIVoiceAudioParams audioParams_out = {
         .audioFormat = MP3,
         .channelCount = 1,
         .sampleRate = 16000
     };
+    
+    // 如果从文件加载了有效配置,则应用配置
+    if (g_tts_audio_setting.valid) {
+        // 转换音频格式字符串为枚举值
+        audioParams_out.audioFormat = ln_ws_convert_audio_format_string_to_enum(g_tts_audio_setting.audio_format);
+        
+        // 应用采样率(如果有效)
+        if (g_tts_audio_setting.audio_sample_rate > 0) {
+            audioParams_out.sampleRate = g_tts_audio_setting.audio_sample_rate;
+        }
+        
+        // 应用声道数(如果有效)
+        if (g_tts_audio_setting.audio_channel > 0) {
+            audioParams_out.channelCount = g_tts_audio_setting.audio_channel;
+        }
+
+        AI_SetTTSAudioBitRate(g_tts_audio_setting.audio_bit_rate);
+        
+        LN_WS_INFO("Applied TTS audio settings: format=%u, sampleRate=%u, channelCount=%u\n",
+               audioParams_out.audioFormat, audioParams_out.sampleRate, audioParams_out.channelCount);
+    } else {
+        LN_WS_DEBUG("Using default audio parameters for audioParams_out\n");
+    }
+
+
 
     #ifdef SUPPORT_CAT1_SERVICE
     cat1_api_keep_wakeup(1);
@@ -1936,6 +1981,8 @@ static ws_command_t ln_ws_parse_command_string(const char *command_str)
         return WS_CMD_CANCEL_DOWNLOAD_EMOJI;
     } else if (strcmp(command_str, "CancelDownloadEmojiAck") == 0) {
         return WS_CMD_CANCEL_DOWNLOAD_EMOJI_ACK;
+    } else if (strcmp(command_str, "TTSAudioSetting") == 0) {
+        return WS_CMD_TTS_AUDIO_SETTING;
     } else {
         return WS_CMD_UNKNOWN;
     }
@@ -2132,6 +2179,64 @@ static int ln_ws_parse_data_message(const char *json_str, ws_data_msg_t *data_ms
                data_msg->finished ? "true" : "false", data_msg->chat_id, data_msg->audio_url, data_msg->text_content, data_msg->audio_duration);
     }
     
+    // 解析TTSAudioSetting特定字段
+    if (data_msg->command == WS_CMD_TTS_AUDIO_SETTING) {
+        cJSON *data_obj = cJSON_GetObjectItem(root, "data");
+        if (data_obj && cJSON_IsObject(data_obj)) {
+            // 清空之前的设置
+            memset(&g_tts_audio_setting, 0, sizeof(tts_audio_setting_t));
+            
+            // 解析audioBitRate字段
+            cJSON *audio_bit_rate = cJSON_GetObjectItem(data_obj, "audioBitRate");
+            if (audio_bit_rate && cJSON_IsNumber(audio_bit_rate)) {
+                g_tts_audio_setting.audio_bit_rate = (uint32_t)audio_bit_rate->valuedouble;
+            } else {
+                LN_WS_WARN("Missing or invalid audioBitRate in TTSAudioSetting\n");
+            }
+            
+            // 解析audioSampleRate字段
+            cJSON *audio_sample_rate = cJSON_GetObjectItem(data_obj, "audioSampleRate");
+            if (audio_sample_rate && cJSON_IsNumber(audio_sample_rate)) {
+                g_tts_audio_setting.audio_sample_rate = (uint32_t)audio_sample_rate->valuedouble;
+            } else {
+                LN_WS_WARN("Missing or invalid audioSampleRate in TTSAudioSetting\n");
+            }
+            
+            // 解析audioChannel字段
+            cJSON *audio_channel = cJSON_GetObjectItem(data_obj, "audioChannel");
+            if (audio_channel && cJSON_IsNumber(audio_channel)) {
+                g_tts_audio_setting.audio_channel = (uint32_t)audio_channel->valuedouble;
+            } else {
+                LN_WS_WARN("Missing or invalid audioChannel in TTSAudioSetting\n");
+            }
+            
+            // 解析audioFormat字段
+            cJSON *audio_format = cJSON_GetObjectItem(data_obj, "audioFormat");
+            if (audio_format && cJSON_IsString(audio_format) && audio_format->valuestring) {
+                strncpy(g_tts_audio_setting.audio_format, audio_format->valuestring, sizeof(g_tts_audio_setting.audio_format) - 1);
+                g_tts_audio_setting.audio_format[sizeof(g_tts_audio_setting.audio_format) - 1] = '\0';
+            } else {
+                LN_WS_WARN("Missing or invalid audioFormat in TTSAudioSetting\n");
+            }
+            
+            // 标记数据有效
+            g_tts_audio_setting.valid = true;
+            
+            // 保存配置到文件
+            if (ln_ws_save_tts_audio_setting_to_file() == 0) {
+                LN_WS_DEBUG("TTSAudioSetting saved to file successfully\n");
+            } else {
+                LN_WS_WARN("Failed to save TTSAudioSetting to file\n");
+            }
+        } else {
+            LN_WS_WARN("Missing or invalid data object in TTSAudioSetting\n");
+        }
+        
+        LN_WS_DEBUG("TTSAudioSetting parsed: bitRate=%u, sampleRate=%u, channel=%u, format=%s\n",
+               g_tts_audio_setting.audio_bit_rate, g_tts_audio_setting.audio_sample_rate, 
+               g_tts_audio_setting.audio_channel, g_tts_audio_setting.audio_format);
+    }
+    
     // 解析ChooseStoryResponse特定字段
     if (data_msg->command == WS_CMD_CHOOSE_STORY_RESPONSE) {
         cJSON *data_obj = cJSON_GetObjectItem(root, "data");
@@ -2450,6 +2555,18 @@ static void ln_ws_default_message_handler(const ws_data_msg_t *data_msg, ws_comm
                 LN_WS_DEBUG("Story Audio URL: %s\n", data_msg->audio_url);
             }
             break;
+        case WS_CMD_TTS_AUDIO_SETTING:
+            if (data_msg) {
+                LN_WS_DEBUG("Default: Processing TTSAudioSetting message\n");
+                if (g_tts_audio_setting.valid) {
+                    LN_WS_DEBUG("TTSAudioSetting details: bitRate=%u, sampleRate=%u, channel=%u, format=%s\n",
+                           g_tts_audio_setting.audio_bit_rate, g_tts_audio_setting.audio_sample_rate,
+                           g_tts_audio_setting.audio_channel, g_tts_audio_setting.audio_format);
+                } else {
+                    LN_WS_WARN("TTSAudioSetting data is invalid\n");
+                }
+            }
+            break;
         case WS_CMD_UNKNOWN:
             if (data_msg) {
                 LN_WS_DEBUG("Default: Processing unknown command message\n");
@@ -3819,4 +3936,192 @@ int ln_ws_send_sleep_mode_inquire(const char *intention)
     }
     
     return ret;
+}
+
+/**
+ * @brief 保存TTS音频设置到文件
+ * @return 0 成功,-1 失败
+ */
+static int ln_ws_save_tts_audio_setting_to_file(void)
+{
+    if (!g_tts_audio_setting.valid) {
+        LN_WS_WARN("TTS audio setting is invalid, cannot save to file\n");
+        return -1;
+    }
+    
+    const char *file_path = "/user/tts_audio_setting.json";
+    
+    // 创建JSON对象
+    cJSON *root = cJSON_CreateObject();
+    if (!root) {
+        LN_WS_ERROR("Failed to create JSON object for TTS audio setting\n");
+        return -1;
+    }
+    
+    cJSON_AddNumberToObject(root, "audioBitRate", g_tts_audio_setting.audio_bit_rate);
+    cJSON_AddNumberToObject(root, "audioSampleRate", g_tts_audio_setting.audio_sample_rate);
+    cJSON_AddNumberToObject(root, "audioChannel", g_tts_audio_setting.audio_channel);
+    cJSON_AddStringToObject(root, "audioFormat", g_tts_audio_setting.audio_format);
+    
+    // 转换为JSON字符串
+    char *json_str = cJSON_Print(root);
+    if (!json_str) {
+        LN_WS_ERROR("Failed to create JSON string for TTS audio setting\n");
+        cJSON_Delete(root);
+        return -1;
+    }
+    
+    // 写入文件
+    FILE *fp = fopen(file_path, "w");
+    if (fp == NULL) {
+        LN_WS_ERROR("Failed to open file for writing: %s\n", file_path);
+        cJSON_free(json_str);
+        cJSON_Delete(root);
+        return -1;
+    }
+    
+    size_t len = strlen(json_str);
+    size_t written = fwrite(json_str, 1, len, fp);
+    fclose(fp);
+    
+    cJSON_free(json_str);
+    cJSON_Delete(root);
+    
+    if (written != len) {
+        LN_WS_ERROR("Failed to write all data to file: %s, written: %zu, expected: %zu\n", 
+                   file_path, written, len);
+        return -1;
+    }
+    
+    LN_WS_DEBUG("TTS audio setting saved to file: %s\n", file_path);
+    return 0;
+}
+
+/**
+ * @brief 从文件加载TTS音频设置
+ * @return 0 成功,-1 失败
+ */
+static int ln_ws_load_tts_audio_setting_from_file(void)
+{
+    const char *file_path = "/user/tts_audio_setting.json";
+    
+    // 打开文件
+    FILE *fp = fopen(file_path, "r");
+    if (fp == NULL) {
+        LN_WS_DEBUG("TTS audio setting file not found: %s, using default values\n", file_path);
+        return -1;
+    }
+    
+    // 获取文件大小
+    fseek(fp, 0, SEEK_END);
+    long file_size = ftell(fp);
+    fseek(fp, 0, SEEK_SET);
+    
+    if (file_size <= 0 || file_size > 1024) {  // 限制文件大小
+        LN_WS_WARN("Invalid file size: %ld\n", file_size);
+        fclose(fp);
+        return -1;
+    }
+    
+    // 读取文件内容
+    char *buffer = (char *)malloc(file_size + 1);
+    if (!buffer) {
+        LN_WS_ERROR("Failed to allocate memory for file content\n");
+        fclose(fp);
+        return -1;
+    }
+    
+    size_t read_size = fread(buffer, 1, file_size, fp);
+    fclose(fp);
+    
+    if (read_size != (size_t)file_size) {
+        LN_WS_WARN("Failed to read all data from file: %s\n", file_path);
+        free(buffer);
+        return -1;
+    }
+    
+    buffer[file_size] = '\0';
+    
+    // 解析JSON
+    cJSON *root = cJSON_Parse(buffer);
+    free(buffer);
+    
+    if (!root) {
+        const char *error_ptr = cJSON_GetErrorPtr();
+        if (error_ptr != NULL) {
+            LN_WS_ERROR("JSON parse error: %s\n", error_ptr);
+        }
+        return -1;
+    }
+    
+    // 清空之前的设置
+    memset(&g_tts_audio_setting, 0, sizeof(tts_audio_setting_t));
+    
+    // 解析字段
+    cJSON *audio_bit_rate = cJSON_GetObjectItem(root, "audioBitRate");
+    if (audio_bit_rate && cJSON_IsNumber(audio_bit_rate)) {
+        g_tts_audio_setting.audio_bit_rate = (uint32_t)audio_bit_rate->valuedouble;
+    }
+    
+    cJSON *audio_sample_rate = cJSON_GetObjectItem(root, "audioSampleRate");
+    if (audio_sample_rate && cJSON_IsNumber(audio_sample_rate)) {
+        g_tts_audio_setting.audio_sample_rate = (uint32_t)audio_sample_rate->valuedouble;
+    }
+    
+    cJSON *audio_channel = cJSON_GetObjectItem(root, "audioChannel");
+    if (audio_channel && cJSON_IsNumber(audio_channel)) {
+        g_tts_audio_setting.audio_channel = (uint32_t)audio_channel->valuedouble;
+    }
+    
+    cJSON *audio_format = cJSON_GetObjectItem(root, "audioFormat");
+    if (audio_format && cJSON_IsString(audio_format) && audio_format->valuestring) {
+        strncpy(g_tts_audio_setting.audio_format, audio_format->valuestring, 
+                sizeof(g_tts_audio_setting.audio_format) - 1);
+        g_tts_audio_setting.audio_format[sizeof(g_tts_audio_setting.audio_format) - 1] = '\0';
+    }
+    
+    // 标记数据有效
+    g_tts_audio_setting.valid = true;
+    
+    cJSON_Delete(root);
+    
+    LN_WS_DEBUG("TTS audio setting loaded from file: %s\n", file_path);
+    LN_WS_DEBUG("Loaded settings: bitRate=%u, sampleRate=%u, channel=%u, format=%s\n",
+           g_tts_audio_setting.audio_bit_rate, g_tts_audio_setting.audio_sample_rate,
+           g_tts_audio_setting.audio_channel, g_tts_audio_setting.audio_format);
+    
+    return 0;
+}
+
+/**
+ * @brief 将音频格式字符串转换为枚举值
+ * @param format_str 格式字符串(如 "mp3", "pcm")
+ * @return AudioCodecFormat 枚举值,如果无效则返回 MP3(默认值)
+ */
+static uint32_t ln_ws_convert_audio_format_string_to_enum(const char *format_str)
+{
+    if (!format_str) {
+        return MP3;  // 默认值
+    }
+    
+    // 转换为小写进行比较
+    char lower_str[16] = {0};
+    for (int i = 0; i < (int)sizeof(lower_str) - 1 && format_str[i] != '\0'; i++) {
+        lower_str[i] = (char)tolower((unsigned char)format_str[i]);
+    }
+    
+    if (strcmp(lower_str, "mp3") == 0) {
+        return MP3;
+    } else if (strcmp(lower_str, "pcm") == 0) {
+        return PCM;
+    } else if (strcmp(lower_str, "aac") == 0 || strcmp(lower_str, "aac_lc") == 0) {
+        return AAC_LC;
+    } else if (strcmp(lower_str, "opus") == 0) {
+        return OPUS;
+    } else if (strcmp(lower_str, "flac") == 0) {
+        return FLAC;
+    } else {
+        LN_WS_WARN("Unknown audio format: %s, using default MP3\n", format_str);
+        return MP3;  // 默认值
+    }
 }
\ No newline at end of file