SendMsgToUIService(UI_UPDATE_AI_ANSWER_STR, 0, nullptr, 0);
}
+ // 安全替换 UTF-8 字符串中的字符
+ static void safe_replace_utf8(std::string &str, const std::string &from_chars, char replacement)
+ {
+ std::string result;
+ result.reserve(str.length()); // 预分配足够空间
+
+ const char *p = str.c_str();
+ const char *end = p + str.length();
+
+ while (p < end)
+ {
+ unsigned char c = *p;
+ size_t char_size = 1;
+
+ // 判断 UTF-8 字符的字节数
+ if ((c & 0x80) == 0)
+ {
+ // 1字节字符 (ASCII)
+ char_size = 1;
+ }
+ else if ((c & 0xE0) == 0xC0)
+ {
+ // 2字节字符
+ char_size = 2;
+ }
+ else if ((c & 0xF0) == 0xE0)
+ {
+ // 3字节字符 (常见中文)
+ char_size = 3;
+ }
+ else if ((c & 0xF8) == 0xF0)
+ {
+ // 4字节字符 (如 Emoji)
+ char_size = 4;
+ }
+ else
+ {
+ // 无效字符,按单字节处理
+ char_size = 1;
+ }
+
+ // 检查字符是否在要替换的集合中
+ bool replace = false;
+ if (char_size == 1)
+ {
+ // 单字节字符直接检查
+ replace = (from_chars.find(c) != std::string::npos);
+ }
+ else
+ {
+ // 多字节字符,提取完整字符并检查
+ std::string utf8_char(p, char_size);
+ for (size_t i = 0; i < from_chars.length();)
+ {
+ // 提取 from_chars 中的下一个 UTF-8 字符
+ unsigned char fc = from_chars[i];
+ size_t fc_size = 1;
+
+ if ((fc & 0x80) == 0)
+ {
+ fc_size = 1;
+ }
+ else if ((fc & 0xE0) == 0xC0)
+ {
+ fc_size = 2;
+ }
+ else if ((fc & 0xF0) == 0xE0)
+ {
+ fc_size = 3;
+ }
+ else if ((fc & 0xF8) == 0xF0)
+ {
+ fc_size = 4;
+ }
+
+ if (i + fc_size <= from_chars.length())
+ {
+ std::string fc_str = from_chars.substr(i, fc_size);
+ if (fc_str == utf8_char)
+ {
+ replace = true;
+ break;
+ }
+ }
+
+ i += fc_size;
+ }
+ }
+
+ // 添加到结果字符串
+ if (replace)
+ {
+ result += replacement;
+ }
+ else
+ {
+ result.append(p, char_size);
+ }
+
+ p += char_size;
+ }
+
+ // 替换原字符串
+ str.swap(result);
+ }
+
+#if 0
+ void AiVoiceModel::ProcessTextByPunctuation()
+ {
+ const std::string punctuation = ",。!?;:))”、"; // 要替换的中文标点
+ const char replacement = ','; // 替换为英文逗号
+ const size_t MAX_SEGMENT_LENGTH = 120; // 最大分段长度
+
+ if (accumulatedText.empty())
+ return;
+
+ // 安全替换中文标点为英文逗号
+ std::string processedText = accumulatedText;
+ safe_replace_utf8(processedText, punctuation, replacement);
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "processedText:%s", processedText.c_str());
+
+ accumulatedText = processedText;
+ size_t commaPos = accumulatedText.find_last_of(',');
+
+ // 处理长度限制,若超过最大长度则强制分段
+ if (accumulatedText.length() >= MAX_SEGMENT_LENGTH)
+ {
+ commaPos = MAX_SEGMENT_LENGTH - 1;
+ // 查找最大长度内最近的逗号
+ size_t lastCommaInRange = accumulatedText.find_last_of(',', commaPos);
+ if (lastCommaInRange != std::string::npos)
+ {
+ commaPos = lastCommaInRange;
+ }
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "commaPos:%d", commaPos);
+
+ if (commaPos != std::string::npos)
+ {
+ // 提取分段(包含英文逗号)
+ std::string segment = accumulatedText.substr(0, commaPos + 1);
+
+ // 清理文本
+ segment.erase(
+ std::remove(segment.begin(), segment.end(), '\n'),
+ segment.end());
+ segment.erase(
+ std::remove(segment.begin(), segment.end(), '\r'),
+ segment.end());
+
+ // 发送到TTS播放
+ if (!segment.empty())
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "播放分段文本: %s (长度: %zu)",
+ segment.c_str(), segment.length());
+ uapi_start_tts_single_txt((char *)segment.c_str());
+ LOS_Msleep(50);
+ }
+
+ // 更新累积文本
+ if (commaPos + 1 < accumulatedText.length())
+ {
+ accumulatedText = accumulatedText.substr(commaPos + 1);
+ }
+ else
+ {
+ accumulatedText.clear();
+ }
+ }
+ else
+ {
+ // 无逗号时继续累积或强制分段
+ if (accumulatedText.length() > MAX_SEGMENT_LENGTH)
+ {
+ // 强制截断(确保在字符边界)
+ std::string segment = accumulatedText.substr(0, MAX_SEGMENT_LENGTH);
+ // 清理文本
+ segment.erase(
+ std::remove(segment.begin(), segment.end(), '\n'),
+ segment.end());
+ segment.erase(
+ std::remove(segment.begin(), segment.end(), '\r'),
+ segment.end());
+ // 发送分段
+ if (!segment.empty())
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "强制分段: %s (长度: %zu)",
+ segment.c_str(), segment.length());
+ uapi_start_tts_single_txt((char *)segment.c_str());
+ LOS_Msleep(50);
+ }
+ // 更新累积文本
+ accumulatedText = accumulatedText.substr(MAX_SEGMENT_LENGTH);
+ }
+ else
+ {
+ // 无逗号且长度不足时继续累积
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "累积文本(未触发): %s", accumulatedText.c_str());
+ }
+ }
+ }
+#endif
+
+ void AiVoiceModel::ProcessTextByPunctuation()
+ {
+ const std::string punctuation = ",。!?;:))”、"; // 要替换的中文标点
+ const char replacement = ','; // 替换为英文逗号
+ const size_t MAX_SEGMENT_LENGTH = 120; // 最大分段长度
+ const size_t MIN_SEGMENT_LENGTH = 1; // 最小发送长度(可调整)
+
+ if (accumulatedText.empty())
+ return;
+
+ // 安全替换中文标点为英文逗号
+ std::string processedText = accumulatedText;
+ safe_replace_utf8(processedText, punctuation, replacement);
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "processedText:%s", processedText.c_str());
+
+ accumulatedText = processedText;
+ size_t commaPos = accumulatedText.find_last_of(',');
+
+ // 处理长度限制,若超过最大长度则强制分段
+ if (accumulatedText.length() >= MAX_SEGMENT_LENGTH)
+ {
+ commaPos = MAX_SEGMENT_LENGTH - 1;
+ // 查找最大长度内最近的逗号
+ size_t lastCommaInRange = accumulatedText.find_last_of(',', commaPos);
+ if (lastCommaInRange != std::string::npos)
+ {
+ commaPos = lastCommaInRange;
+ }
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "commaPos:%d", commaPos);
+
+ if (commaPos != std::string::npos)
+ {
+ // 按标点分段发送
+ sendSegment(accumulatedText, commaPos);
+ }
+ else
+ {
+ // 无逗号时的处理逻辑
+ if (accumulatedText.length() >= MIN_SEGMENT_LENGTH)
+ {
+ // 强制发送完整文本(无论是否有标点)
+ std::string segment = accumulatedText;
+ segment.erase(
+ std::remove(segment.begin(), segment.end(), '\n'),
+ segment.end());
+ segment.erase(
+ std::remove(segment.begin(), segment.end(), '\r'),
+ segment.end());
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "强制发送文本: %s (长度: %zu)",
+ segment.c_str(), segment.length());
+ uapi_start_tts_single_txt((char *)segment.c_str());
+ LOS_Msleep(50);
+
+ // 发送后清空累积文本
+ accumulatedText.clear();
+ }
+ else
+ {
+ // 文本过短,继续累积
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "累积文本过短(长度: %zu), 继续累积",
+ accumulatedText.length());
+ }
+ }
+ }
+
+ // 辅助函数:按标点分段发送
+ void AiVoiceModel::sendSegment(const std::string &text, size_t pos)
+ {
+ // 提取分段(包含英文逗号)
+ std::string segment = text.substr(0, pos + 1);
+
+ // 清理文本
+ segment.erase(
+ std::remove(segment.begin(), segment.end(), '\n'),
+ segment.end());
+ segment.erase(
+ std::remove(segment.begin(), segment.end(), '\r'),
+ segment.end());
+
+ // 发送到TTS播放
+ if (!segment.empty())
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "按标点发送分段: %s (长度: %zu)",
+ segment.c_str(), segment.length());
+ uapi_start_tts_single_txt((char *)segment.c_str());
+ LOS_Msleep(50);
+ }
+
+ // 更新累积文本
+ if (pos + 1 < text.length())
+ {
+ accumulatedText = text.substr(pos + 1);
+ }
+ else
+ {
+ accumulatedText.clear();
+ }
+ }
+
+ std::string AiVoiceModel::GetAccumulatedText(void)
+ {
+ return accumulatedText;
+ }
+
void AiVoiceModel::SetAiAnswerStrStr(std::string str, uint8_t type)
{
AIResponseType = type;
if (!OHOS::AiVoiceModel::GetInstance().GetIsLimitDisplayed()) // 如果ai答案没有显示到极限了,就更新ai答案字符串
{
aiAnswerStr += str;
+ /* -------------------------------------------------------------------------- */
+ // 累积接收到的文本
+ accumulatedText += str;
+ /* -------------------------------------------------------------------------- */
WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "接受字的符串:%s", aiAnswerStr.c_str());
SendMsgToUIService(UI_UPDATE_AI_ANSWER_STR, 0, nullptr, 0);
}
WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "清空上次用户问题和Ai回答字符串");
userQuestionStr = "";
aiAnswerStr = "";
+ accumulatedText = "";
}
/*获取AI音频路径*/
/* -------------------------------- tts播放定时器 -------------------------------- */
static osTimerId_t g_ai4g_is_start_tts_play_Handle = nullptr;
- static uint8_t AI4G_IS_START_TTS_PLAY_TIME = 1;
-
+ static constexpr const int8_t NUMBER_TEN = 15;
+ static int8_t AI4G_IS_START_TTS_PLAY_CNT = NUMBER_TEN;
+ static uint8_t tts_record_flag = 0;
static void AI4G_IS_START_TTS_CallbackProc(int data)
{
- if (AI4G_IS_START_TTS_PLAY_TIME <= 0)
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "AI4G_IS_START_TTS_CallbackProc:%s", AiVoiceModel::GetInstance().GetAccumulatedText().c_str());
+ // 用来判断是否需要停止发送文本
+ if (AiVoiceModel::GetInstance().GetAccumulatedText().empty())
{
AiVoiceModel::GetInstance().stop_ai4g_tts_play_timer();
- std::string str = AiVoiceModel::GetInstance().GetAiAnswerStr();
- // 移除所有换行符(包括 '\n' 和 '\r')
- str.erase(
- std::remove(str.begin(), str.end(), '\n'),
- str.end());
- str.erase(
- std::remove(str.begin(), str.end(), '\r'),
- str.end());
- uapi_start_tts_single_txt((char *)str.c_str());
}
+ if (get_bk_websocket_tts_tick() == 0) // 代表可以发录音文本
+ {
+ AiVoiceModel::GetInstance().ProcessTextByPunctuation();
+ }
+ else
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "get_bk_websocket_tts_tick:%d", get_bk_websocket_tts_tick());
+ }
+#if TTS_TEST
+ if (!tts_record_flag)
+ {
+ tts_record_flag = 1;
+ startAudioCaptureTask(); // 测试汇声抑制的
+ }
+#endif
}
static void AI4G_IS_START_TTS_Callback(int data)
{
- AI4G_IS_START_TTS_PLAY_TIME--;
AI4G_IS_START_TTS_CallbackProc(data);
// GraphicService::GetInstance()->PostGraphicEvent(std::bind(AI4G_IS_START_TTS_CallbackProc, data));
}
void AiVoiceModel::start_ai4g_tts_play_timer(uint32_t interval)
{
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "AiVoiceModel::start_ai_isready_timer");
- AI4G_IS_START_TTS_PLAY_TIME = 3;
+ // AI4G_IS_START_TTS_PLAY_CNT = NUMBER_TEN;
/* -------------------------- 1.检查定时器是否已经存在,如果存在则返回 ------------------------- */
if (g_ai4g_is_start_tts_play_Handle != nullptr)
{
// 检查定时器是否存在
if (g_ai4g_is_start_tts_play_Handle != nullptr)
{
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "AiVoiceModel::stop_ai_isready_timer");
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "stop_ai4g_tts_play_timer");
osTimerStop(g_ai4g_is_start_tts_play_Handle);
// 删除定时器并置为 nullptr
osStatus_t ret = osTimerDelete(g_ai4g_is_start_tts_play_Handle);
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "osTimerDelete failed, ret = %lx!!", ret);
}
g_ai4g_is_start_tts_play_Handle = nullptr;
- AI4G_IS_START_TTS_PLAY_TIME = 1;
+ AI4G_IS_START_TTS_PLAY_CNT = NUMBER_TEN;
+#if TTS_TEST
+ tts_record_flag = 0;
+ std::string filePath = "/user/aiRecord456.mp3";
+ // 删除filePath文件
+ if (remove(filePath.c_str()) != 0)
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "remove failed, path: %s", filePath.c_str());
+ }
+ StopListenAndRecordingUserQuestion();
+#endif
}
}
add_4GAI_vote(); // 4g投票
uapi_ble_connect_param_update(BLE_CONNECT_INTERVAL_HIGH_AI);
+ Audio::AudioManager::GetInstance().SetVolume(AUDIO_STREAM_VOICE_ASSISTANT, 100);
}
// 页面已经展示后的调用逻辑
AiVoiceModel::GetInstance().ClearQuestionAndAiAnswerStr(); // 清空上次用户问题和Ai回答字符串
AiVoiceModel::GetInstance().SetIsClickable(false);
AiVoiceModel::GetInstance().SetIsLimitDisplayed(false);
+ AiVoiceModel::GetInstance().stop_ai4g_tts_play_timer();
break;
default:
// Handle other cases if needed
PlayersModel::GetInstance()->PlayerPause(); // 暂停手机音乐
if (model->GetSelect4gorbt() == AI_SELECT_4G)
{
+ AiVoiceModel::GetInstance().stop_ai4g_tts_play_timer();
uapi_UI_start_asr();
usleep(50000);
ChangeViewAndLogicOperator(AIVOICE_RETURN_RESULT_TEXT_VIEWID, AIVOICE_AIMODE_CONNECTING);
{
if (model->GetSelect4gorbt() == AI_SELECT_4G)
{
+ AiVoiceModel::GetInstance().stop_ai4g_tts_play_timer();
// model->cancel_tts_play();
dinit_ai_task();
usleep(50000);
if (slider)
{
int value = slider->GetValue();
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "volume:%d front", value);
- if (event.GetRotate() > 0)
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "volume:%d front", value);
+ if (event.GetRotate() <= 0)
{
WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "222");
value += 20;
value = 0;
}
}
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "volume:%d end", value);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "volume:%d end", value);
do_custom_cmd_service_check_msg(CUSTOM_CMD_MSG_TYPE_CHANGE_SYSTEM_VOLUME, (void *)(&value), sizeof(value));
}
}
bool AiVoicePresenter::OnRotate(UIView &view, const RotateEvent &event)
{
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "AiVoicePresenter::OnRotateEnd");
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "AiVoicePresenter::OnRotate");
+ static uint32_t lastCallTime = 0;
+ const uint32_t MIN_INTERVAL = 500; // 最小间隔500ms
+
+ uint32_t currentTime = event.GetTimeStamp();
+
+ // 计算时间间隔
+ uint32_t interval = currentTime - lastCallTime;
+
+ // 仅当间隔大于500ms时才调用ChangeVolume
+ if (interval >= MIN_INTERVAL)
+ {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "AiVoicePresenter::OnRotate: 允许调用ChangeVolume, 间隔: %ums", interval);
+ ChangeVolume(view, event);
+ lastCallTime = currentTime;
+ }
+ else
+ {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "AiVoicePresenter::OnRotate: 忽略调用ChangeVolume, 间隔: %ums (需要>500ms)", interval);
+ }
+
return true;
}