862 lines
29 KiB
C++
862 lines
29 KiB
C++
/*----------------------------------------------------------------------------
|
||
* Copyright (c) TJD Technologies Co., Ltd. 2024. All rights reserved.
|
||
*
|
||
* Description: TjdUiAppPlayDialModel.cpp
|
||
*
|
||
* Author: liuguanwu
|
||
*
|
||
* Create: 2024-10
|
||
*--------------------------------------------------------------------------*/
|
||
#include "TjdUiAppPlayDialModel.h"
|
||
#include "TjdUiAppPlayDialPresenter.h"
|
||
#include "TjdUiAppPlayDialView.h"
|
||
#include "TjdUiImageIds.h"
|
||
#include "TjdUiScreenManage.h"
|
||
#include "audio_capturer.h"
|
||
#include "ble_port_protocol.h"
|
||
#include "cJSON.h"
|
||
#include "common/image_cache_manager.h"
|
||
#include "graphic_service.h"
|
||
#include "hal_tick.h"
|
||
#include "power_display_service.h"
|
||
#include "rtc_api.h"
|
||
#include "sql_setting.h"
|
||
#include "sys_config.h"
|
||
#include "sys_typedef.h"
|
||
#include "thread_adapter.h"
|
||
#include <algorithm>
|
||
#include <atomic>
|
||
#include <cstring>
|
||
#include <dirent.h>
|
||
#include <fstream>
|
||
#include <iomanip>
|
||
#include <iostream>
|
||
#include <sstream>
|
||
#include <stdio.h>
|
||
#include <string>
|
||
#include <vector>
|
||
|
||
namespace TJD {
|
||
|
||
#define ENABLE_PRINT_INFO 0
|
||
#if ENABLE_PRINT_INFO
|
||
#define static_print_info(...) sys_ui_log_i(__VA_ARGS__) // 一般信息打印宏控制
|
||
#define static_print_warn(...) sys_ui_log_w(__VA_ARGS__) // 警告信息打印一般常开
|
||
#define static_print_debug(...) sys_ui_log_d(__VA_ARGS__) // 调试信息打印一般常开
|
||
#define static_print_error(...) sys_ui_log_e(__VA_ARGS__) // 错误信息打印一般常开
|
||
#else
|
||
#define static_print_info(...)
|
||
#define static_print_warn(...)
|
||
#define static_print_error(...)
|
||
#define static_print_debug(...)
|
||
#endif
|
||
|
||
#define LOAD_BUFFER_SIZE 8 * 1024
|
||
#define LEFUN_AI_STORY_FILE_PATH "/user/tjd_wfp/lefun_ai_record.json" // TJD_FS_DIR_WFP
|
||
#define TJD_DIAL_WF_CONFIG_PATH "/user/tjd_wf/wf_config_0.json"
|
||
#define TJD_FS_DIR_WFP_E TJD_FS_DIR_WFP "/"
|
||
|
||
static MutexId g_lefun_ai_mutex = 0;
|
||
static bool story_question_flag = false;
|
||
static bool story_answer_flag = false;
|
||
static struct data_flow *data_flow = nullptr;
|
||
static std::shared_ptr<OHOS::Audio::AudioCapturer> lefunAiCapturer_{nullptr};
|
||
std::string g_PlayDialQuestion = "";
|
||
std::string g_PlayDialAnswer = "";
|
||
std::string g_PlayDialPath = "";
|
||
std::string g_PlayDialPreviewPath = "";
|
||
extern osThreadId_t resend_audio_data_id;
|
||
|
||
TjdUiAppPlayDialModel::TjdUiAppPlayDialModel() { wfFileList.clear(); }
|
||
|
||
TjdUiAppPlayDialModel::~TjdUiAppPlayDialModel()
|
||
{
|
||
for (int16_t i = 0; i < wfFileList.size(); i++) {
|
||
OHOS::ImageCacheManager::GetInstance().UnloadSingleRes(wfFileList.data()[i]);
|
||
}
|
||
wfFileList.clear();
|
||
}
|
||
|
||
int TjdUiAppPlayDialModel::FileExists(const char *filename)
|
||
{
|
||
FILE *file = fopen(filename, "r");
|
||
if (file) {
|
||
fclose(file);
|
||
return 1; // 文件存在
|
||
}
|
||
return 0; // 文件不存在
|
||
}
|
||
|
||
int TjdUiAppPlayDialModel::FileDelete(const char *filename) { return unlink(filename); }
|
||
|
||
void TjdUiAppPlayDialModel::addRecord(History *history, std::string question, std::string answer)
|
||
{
|
||
if (history == nullptr || history->records == nullptr) {
|
||
return;
|
||
}
|
||
|
||
if (history->total_records >= MAX_HISTORY) {
|
||
for (int i = 1; i < MAX_HISTORY; i++) {
|
||
history->records[i - 1] = history->records[i];
|
||
}
|
||
history->total_records--;
|
||
}
|
||
|
||
if (history->total_records < MAX_HISTORY) {
|
||
history->records[history->total_records].question = question;
|
||
history->records[history->total_records].answer = answer;
|
||
history->total_records++;
|
||
}
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::saveToJson(const History *history, const char *filename)
|
||
{
|
||
// 检查输入参数
|
||
if (!history || history->total_records < 0) {
|
||
printf("Error: Invalid history object\n");
|
||
return;
|
||
}
|
||
|
||
// 创建 JSON 对象
|
||
cJSON *json = cJSON_CreateObject();
|
||
|
||
if (!json) {
|
||
printf("Error creating JSON object\n");
|
||
return;
|
||
}
|
||
|
||
// 创建历史记录数组
|
||
|
||
cJSON *historyArray = cJSON_CreateArray();
|
||
if (!historyArray) {
|
||
printf("Error creating JSON array\n");
|
||
cJSON_Delete(json);
|
||
return;
|
||
}
|
||
|
||
// 将 total_records 添加到 JSON 对象中
|
||
cJSON_AddNumberToObject(json, "total_records", history->total_records);
|
||
|
||
// 遍历历史记录并添加到 JSON 数组中
|
||
for (int i = 0; i < history->total_records; i++) {
|
||
const std::string &question = history->records[i].question;
|
||
const std::string &answer = history->records[i].answer;
|
||
|
||
cJSON *item = cJSON_CreateObject();
|
||
|
||
if (!item) {
|
||
printf("Error creating JSON item for record \n");
|
||
continue;
|
||
}
|
||
|
||
if (item == nullptr) {
|
||
return;
|
||
}
|
||
|
||
cJSON_AddStringToObject(item, "question", question.c_str());
|
||
cJSON_AddStringToObject(item, "answer", answer.c_str());
|
||
|
||
// 将记录添加到历史记录数组
|
||
cJSON_AddItemToArray(historyArray, item);
|
||
}
|
||
|
||
// 将历史记录数组添加到 JSON 对象
|
||
cJSON_AddItemToObject(json, "history", historyArray);
|
||
|
||
// 打开文件以写入 JSON 数据
|
||
FILE *file = fopen(filename, "w");
|
||
if (!file) {
|
||
printf("Error opening file: ");
|
||
cJSON_Delete(json);
|
||
return;
|
||
}
|
||
|
||
// 将 JSON 对象转换为字符串并写入文件
|
||
char *jsonString = cJSON_Print(json);
|
||
|
||
if (jsonString) {
|
||
fprintf(file, "%s\n", jsonString);
|
||
free(jsonString); // 释放 JSON 字符串
|
||
}
|
||
|
||
// 关闭文件和删除 JSON 对象
|
||
fclose(file);
|
||
cJSON_Delete(json);
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::displayHistory(const History *history)
|
||
{
|
||
printf("displayHistory %d\n", history->total_records);
|
||
for (int i = 0; i < history->total_records; i++) {
|
||
// 打印每个记录的 question 和 answer
|
||
printf("Record %d:\n", i + 1);
|
||
printf("Question: %s\n", history->records[i].question.c_str());
|
||
printf("Answer: %s\n", history->records[i].answer.c_str());
|
||
printf("------------------\n");
|
||
}
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::loadFromJson(History *history)
|
||
{
|
||
char *filename = LEFUN_AI_STORY_FILE_PATH;
|
||
FILE *file = fopen(filename, "r");
|
||
if (!file) {
|
||
// 文件不存在,创建初始 JSON
|
||
createInitialJson(filename);
|
||
return; // 返回以避免进一步处理
|
||
}
|
||
char *buffer = (char *)malloc(LOAD_BUFFER_SIZE);
|
||
if (!buffer) {
|
||
printf("Error: Failed to allocate memory for buffer.\n");
|
||
fclose(file);
|
||
return;
|
||
}
|
||
|
||
size_t bytesRead = fread(buffer, sizeof(char), LOAD_BUFFER_SIZE - 1, file);
|
||
buffer[bytesRead] = '\0';
|
||
fclose(file);
|
||
|
||
cJSON *json = cJSON_Parse(buffer);
|
||
if (json) {
|
||
|
||
cJSON *totalRecordsItem = cJSON_GetObjectItem(json, "total_records");
|
||
history->total_records = totalRecordsItem ? totalRecordsItem->valueint : 0;
|
||
cJSON *historyArray = cJSON_GetObjectItem(json, "history");
|
||
cJSON *record = NULL;
|
||
int index = 0; // 当前存储记录的索引
|
||
|
||
cJSON_ArrayForEach(record, historyArray)
|
||
{
|
||
if (index < 10) {
|
||
|
||
// 获取问题和答案
|
||
cJSON *questionItem = cJSON_GetObjectItem(record, "question");
|
||
cJSON *answerItem = cJSON_GetObjectItem(record, "answer");
|
||
|
||
if (questionItem && questionItem->valuestring && answerItem && answerItem->valuestring) {
|
||
history->records[index].question = questionItem->valuestring;
|
||
history->records[index].answer = answerItem->valuestring;
|
||
index++;
|
||
} else {
|
||
fprintf(stderr, "Warning: Missing question or answer in record %d\n", index);
|
||
}
|
||
}
|
||
|
||
history->total_records = index;
|
||
}
|
||
cJSON_Delete(json);
|
||
} else {
|
||
static_print_warn("Error parsing JSON json null\n");
|
||
}
|
||
|
||
free(buffer);
|
||
}
|
||
|
||
uint8_t TjdUiAppPlayDialModel::get_total_records_from_json()
|
||
{
|
||
FILE *file = fopen(LEFUN_AI_STORY_FILE_PATH, "r");
|
||
if (file == NULL) {
|
||
printf("Could not open file %s\n", LEFUN_AI_STORY_FILE_PATH);
|
||
return 0; // 返回 0 表示文件未未初始化,直接返回
|
||
}
|
||
|
||
// 获取文件大小
|
||
fseek(file, 0, SEEK_END);
|
||
long length = ftell(file);
|
||
fseek(file, 0, SEEK_SET);
|
||
|
||
// 读取文件内容
|
||
char *data = (char *)malloc(length + 1);
|
||
fread(data, 1, length, file);
|
||
data[length] = '\0'; // 确保字符串以 null 结尾
|
||
fclose(file);
|
||
|
||
cJSON *json = cJSON_Parse(data);
|
||
free(data);
|
||
|
||
if (json == NULL) {
|
||
printf("Error parsing JSON\n");
|
||
return -1; // 返回 -1 表示解析错误
|
||
}
|
||
|
||
cJSON *total_records_item = cJSON_GetObjectItem(json, "total_records");
|
||
uint8_t total_records = total_records_item ? total_records_item->valueint : -1; // 默认返回 -1
|
||
|
||
cJSON_Delete(json);
|
||
return total_records;
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::createInitialJson(const char *filename)
|
||
{
|
||
cJSON *json = cJSON_CreateObject();
|
||
cJSON_AddNumberToObject(json, "total_records", 0);
|
||
cJSON *historyArray = cJSON_CreateArray();
|
||
cJSON_AddItemToObject(json, "history", historyArray);
|
||
|
||
FILE *file = fopen(filename, "w");
|
||
if (file) {
|
||
char *jsonString = cJSON_Print(json);
|
||
fprintf(file, "%s\n", jsonString);
|
||
free(jsonString);
|
||
fclose(file);
|
||
}
|
||
|
||
cJSON_Delete(json);
|
||
}
|
||
|
||
uint8_t TjdUiAppPlayDialModel::UpdateHistoryFileList(uint8_t max_file)
|
||
{
|
||
std::string fullPath;
|
||
|
||
for (int16_t i = 0; i < wfFileList.size(); i++) {
|
||
OHOS::ImageCacheManager::GetInstance().UnloadSingleRes(wfFileList.data()[i]);
|
||
}
|
||
wfFileList.clear();
|
||
|
||
// 云表盘:打开目录
|
||
struct dirent *direntp;
|
||
DIR *dirp = opendir(TJD_FS_DIR_WFP);
|
||
// 遍历文件
|
||
if (dirp != nullptr) {
|
||
std::string DirPrePath(TJD_FS_DIR_WFP);
|
||
while ((direntp = readdir(dirp)) != nullptr) {
|
||
std::string filename(direntp->d_name);
|
||
fullPath = TJD_FS_DIR_WFP_E + filename;
|
||
printf("filename(%s-%s)\n", filename.c_str(), fullPath.c_str());
|
||
if (filename.find(".bin") != std::string::npos && filename.find("wfp_bg") != std::string::npos) {
|
||
std::string number;
|
||
std::size_t pos1 = filename.find_last_of('_');
|
||
if (pos1 != std::string::npos) {
|
||
// 查找 .bin 的位置
|
||
std::size_t pos2 = filename.find(".bwf", pos1);
|
||
if (pos2 != std::string::npos) {
|
||
// 提取下划线和 .bin 之间的子字符串
|
||
number = filename.substr(pos1 + 1, pos2 - pos1 - 1);
|
||
static_print_debug("FindWfFile number: %s\n", number.c_str());
|
||
}
|
||
}
|
||
wfFileList.push_back(fullPath);
|
||
printf(".bin filename(%s)\n", fullPath.c_str());
|
||
} else if (filename.find(".hbm") != std::string::npos &&
|
||
filename.find("wfp_preview") != std::string::npos) {
|
||
printf(".hbm filename(%s)\n", fullPath.c_str());
|
||
} else {
|
||
FileDelete(fullPath.c_str());
|
||
}
|
||
}
|
||
}
|
||
// 关闭目录
|
||
closedir(dirp);
|
||
|
||
std::sort(wfFileList.begin(), wfFileList.end(), std::greater<std::string>());
|
||
while (wfFileList.size() > max_file) {
|
||
FileDelete(wfFileList[wfFileList.size() - 1].c_str());
|
||
wfFileList.pop_back();
|
||
}
|
||
printf("wfFileList.size(%d)\n", wfFileList.size());
|
||
return wfFileList.size();
|
||
}
|
||
|
||
std::vector<std::string> *TjdUiAppPlayDialModel::GetHistoryFileList() { return &wfFileList; }
|
||
|
||
uint8_t TjdUiAppPlayDialModel::MakeingWfConfig0Json(const char *filename)
|
||
{
|
||
if (FileExists(filename) == 0) {
|
||
static_print_error("Error opening file:[%s]", filename);
|
||
return -1;
|
||
}
|
||
|
||
std::string fullPath = filename;
|
||
if (strcmp(filename, PLAY_DIAL_DRAW_BIN_TEMP_PATH) == 0) {
|
||
uint64_t timestamp = 0;
|
||
tjd_driver_rtc_get_ops()->get_timestamp(×tamp);
|
||
fullPath.clear();
|
||
fullPath.append(TJD_FS_DIR_WFP);
|
||
fullPath.append("/wfp_bg_");
|
||
fullPath.append(std::to_string(timestamp));
|
||
fullPath.append(".bin");
|
||
printf("g_PlayDialPreviewPath rename():%s,%s\n", filename, fullPath.c_str());
|
||
// PLAY_DIAL_DRAW_BIN_PATH 不使用固定文件名
|
||
if (0 != rename(filename, fullPath.c_str())) {
|
||
printf("TjdUiAppPlayDialModel:fail rename():%s,%s\n", filename, fullPath.c_str());
|
||
fullPath.clear();
|
||
return -1;
|
||
}
|
||
}
|
||
// 创建 JSON 对象
|
||
cJSON *json = cJSON_CreateObject();
|
||
if (!json) {
|
||
static_print_error("Error creating JSON object");
|
||
fullPath.clear();
|
||
return -1;
|
||
}
|
||
// 将 wf_type 添加到 JSON 对象中
|
||
cJSON_AddNumberToObject(json, "wf_type", 0);
|
||
// cJSON_AddNumberToObject(json, "wf_type", param_data.setting_mode);
|
||
// 将 wf_preview 添加到 JSON 对象中
|
||
cJSON_AddStringToObject(json, "wf_preview", fullPath.c_str());
|
||
// 将 bg_res 添加到 JSON 对象中
|
||
cJSON_AddStringToObject(json, "bg_res", fullPath.c_str());
|
||
// 将 img_number 添加到 JSON 对象中
|
||
cJSON_AddNumberToObject(json, "img_number", 1);
|
||
// 将 video_preview 添加到 JSON 对象中
|
||
// cJSON_AddStringToObject(json, "video_preview", TJD_CUSTOM_DIAL_VIDEO_PREVIEW_PATH);
|
||
cJSON_AddNullToObject(json, "video_preview");
|
||
// tjd_set_custom_dial_file_num(0);
|
||
// tjd_set_custom_dial_file_mask(false);
|
||
fullPath.clear();
|
||
// 打开文件以写入 JSON 数据
|
||
FILE *file = fopen(TJD_DIAL_WF_CONFIG_PATH, "w");
|
||
if (!file) {
|
||
static_print_error("Error opening file: ");
|
||
cJSON_Delete(json);
|
||
return -1;
|
||
}
|
||
// 将 JSON 对象转换为字符串并写入文件
|
||
char *jsonString = cJSON_Print(json);
|
||
|
||
if (jsonString) {
|
||
fprintf(file, "%s\n", jsonString);
|
||
free(jsonString); // 释放 JSON 字符串
|
||
}
|
||
|
||
// 关闭文件和删除 JSON 对象
|
||
fclose(file);
|
||
cJSON_Delete(json);
|
||
|
||
return 0;
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::AudioInit(void)
|
||
{
|
||
if (audioInitStatus_) {
|
||
static_print_info("PlayDialModel audioInitStatus_ is true");
|
||
return;
|
||
}
|
||
Audio::AudioManager &amIntance = Audio::AudioManager::GetInstance();
|
||
audioInitStatus_ = amIntance.Initialize();
|
||
playerCtr_ = std::make_shared<OHOS::Media::Player>();
|
||
// playerCallback_ = std::make_shared<RecordPlayerCallbackImpl>();
|
||
static_print_info("PlayDialModel AudioInit end!");
|
||
}
|
||
|
||
int TjdUiAppPlayDialModel::RecordStart()
|
||
{
|
||
printf("TjdUiAppPlayDialModel::RecordStart()\n");
|
||
if (recordIsRuning_) {
|
||
static_print_warn("PlayDialModel RecordStart failed, recordIsRuning_ is true");
|
||
return OHOS_FAILURE;
|
||
}
|
||
|
||
Audio::AudioManager &amIntance = Audio::AudioManager::GetInstance();
|
||
if (!isActivateAudioInterrupt_) {
|
||
static_print_info("ActivateAudioInterrupt");
|
||
// 申请焦点
|
||
sessionId_ = amIntance.MakeSessionId();
|
||
if (sessionId_ == AUDIO_SESSION_ID_NONE) {
|
||
static_print_warn("session invalid");
|
||
return OHOS_FAILURE;
|
||
}
|
||
std::shared_ptr<PlayDialInterruptListener> recordInterruptListener_ =
|
||
std::make_shared<PlayDialInterruptListener>();
|
||
interrupt_ = {AUDIO_STREAM_VOICE_RECORD, sessionId_, recordInterruptListener_};
|
||
// 激活音频中断 5s超时
|
||
if (amIntance.ActivateAudioInterrupt(interrupt_) == INTERRUPT_FAILED) {
|
||
static_print_warn("ActivateAudioInterrupt faild");
|
||
return OHOS_FAILURE;
|
||
}
|
||
isActivateAudioInterrupt_ = true;
|
||
}
|
||
|
||
lefunAiCapturer_ = std::make_shared<OHOS::Audio::AudioCapturer>();
|
||
|
||
url_ = std::string("/user/") + "lefunAi_test.opus";
|
||
|
||
pfd_.open(url_, std::ios::out | std::ios::binary);
|
||
if (!pfd_.is_open()) {
|
||
static_print_warn("open file failed");
|
||
lefunAiCapturer_.reset();
|
||
(void)amIntance.DeactivateAudioInterrupt(interrupt_);
|
||
return -1;
|
||
}
|
||
|
||
// 设置音频参数
|
||
OHOS::Audio::AudioCapturerInfo info = {};
|
||
info.inputSource = AUDIO_MIC;
|
||
info.audioFormat = AudioCodecFormat::OPUS;
|
||
info.sampleRate = 16000;
|
||
info.channelCount = 1;
|
||
info.streamType = AUDIO_STREAM_VOICE_RECORD;
|
||
info.bitWidth = AudioBitWidth::BIT_WIDTH_16;
|
||
info.sessionID = sessionId_;
|
||
|
||
if (lefunAiCapturer_->SetCapturerInfo(info) != 0) {
|
||
static_print_warn("{%s,%d} set capturer info failed", __func__, __LINE__);
|
||
(void)amIntance.DeactivateAudioInterrupt(interrupt_);
|
||
return -1;
|
||
}
|
||
|
||
// 获取每帧大小
|
||
size_t frameCount = lefunAiCapturer_->GetFrameCount();
|
||
if (!frameCount) {
|
||
static_print_warn("Can't GetFrameCount");
|
||
(void)lefunAiCapturer_->Release();
|
||
(void)amIntance.DeactivateAudioInterrupt(interrupt_);
|
||
return -1;
|
||
}
|
||
|
||
framesize_ = frameCount * info.channelCount * info.bitWidth / 8;
|
||
static_print_info("framesize_:%d", framesize_);
|
||
lefunAiRecordBuffer_ = new uint8_t[framesize_];
|
||
if (!lefunAiCapturer_->Start()) {
|
||
static_print_warn("{%s,%d} start failed", __func__, __LINE__);
|
||
lefunAiCapturer_.reset();
|
||
(void)amIntance.DeactivateAudioInterrupt(interrupt_);
|
||
// delete lefunAiCapturer_;
|
||
return -1;
|
||
}
|
||
|
||
recordIsRuning_ = false;
|
||
MediaThreadattr attr = {"lefun_ai_record", 0x2000, THREAD_SCHED_INVALID, 0, true};
|
||
threadIdHandle_ = MediaThreadCreate(
|
||
[](void *arg) -> void * {
|
||
TjdUiAppPlayDialModel::GetInstance().AudioCaptureProcess();
|
||
return nullptr;
|
||
},
|
||
NULL, &attr);
|
||
if (threadIdHandle_ == nullptr) {
|
||
static_print_warn("thread create failed");
|
||
return -1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::RecordStop()
|
||
{
|
||
printf("TjdUiAppPlayDialModel::RecordStop()\n");
|
||
if (recordIsRuning_) {
|
||
{
|
||
std::lock_guard<std::mutex> lock(pauseMutex_);
|
||
needStop_.store(true);
|
||
needPause_.store(false); // 确保线程不会因为暂停而阻塞
|
||
}
|
||
pauseCondVar_.notify_all(); // 唤醒等待的线程
|
||
static_print_debug("LefunAi RecordStop wait thread join");
|
||
osThreadJoin(threadIdHandle_);
|
||
static_print_debug("LefunAi RecordStop thread join end");
|
||
}
|
||
|
||
LefunAiRequestEndAudioTransmit();
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::AudioCaptureProcess(void)
|
||
{
|
||
static_print_info("TjdUiAppPlayDialModel AudioCaptureProcess start");
|
||
recordIsRuning_ = true;
|
||
while (!needStop_) {
|
||
// 检查是否需要暂停和停止
|
||
std::unique_lock<std::mutex> lock(pauseMutex_);
|
||
uint64_t pauseStartTime = OHOS::HALTick::GetInstance().GetTime();
|
||
pauseCondVar_.wait(lock, [this] { return !needPause_ || needStop_; });
|
||
if (needStop_) {
|
||
static_print_debug("needStop_ is true");
|
||
break;
|
||
}
|
||
|
||
memset(lefunAiRecordBuffer_, 0, framesize_);
|
||
ret = lefunAiCapturer_->Read(lefunAiRecordBuffer_, framesize_, false);
|
||
printf("lefunAiCapturer_->Read ret:%d\n", ret);
|
||
if (ret == -1) {
|
||
static_print_warn("audioCap Read failed:0x%x", ret);
|
||
continue;
|
||
}
|
||
if (ret == OHOS::Media::ERR_RETRY_READ) {
|
||
usleep(10000);
|
||
continue;
|
||
}
|
||
|
||
pfd_.write((const char *)lefunAiRecordBuffer_, ret);
|
||
if (!pfd_) {
|
||
static_print_warn("errno:%d, errmsg:%s", errno, strerror(errno));
|
||
break;
|
||
}
|
||
TransmitAudioData(lefunAiRecordBuffer_ + 8, ret - 8);
|
||
}
|
||
|
||
static_print_info("AudioCaptureProcess end");
|
||
pfd_.close();
|
||
RecordDestroy();
|
||
}
|
||
|
||
static void resend_audio_task(void *argument)
|
||
{
|
||
osDelay(500);
|
||
// 打开文件
|
||
std::ifstream inputFile("/user/lefunAi_test.opus", std::ios::in | std::ios::binary);
|
||
if (!inputFile.is_open()) {
|
||
printf("Failed to open file: %s", "/user/lefunAi_test.opus");
|
||
return;
|
||
}
|
||
|
||
// 定义缓冲区,每次读取168字节
|
||
const size_t bufferSize = 168;
|
||
uint8_t buffer[168];
|
||
|
||
// 循环读取文件并发送数据
|
||
while (inputFile.read(reinterpret_cast<char *>(buffer), bufferSize)) {
|
||
size_t bytesRead = inputFile.gcount(); // 获取实际读取的字节数
|
||
|
||
if (bytesRead > 8) {
|
||
// 只传输从第9个字节开始的数据
|
||
printf("Read %zu bytes from file\n", bytesRead);
|
||
TjdUiAppPlayDialModel::GetInstance().TransmitAudioData(buffer + 8,
|
||
bytesRead - 8); // 发送数据(跳过前8个字节)
|
||
osDelay(10); // 等待10ms,防止数据过快导致传输失败
|
||
}
|
||
}
|
||
|
||
printf("AudioCaptureFromFile end");
|
||
inputFile.close();
|
||
TjdUiAppPlayDialModel::GetInstance().LefunAiRequestEndAudioTransmit();
|
||
osThreadTerminate(resend_audio_data_id);
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::ResendAudioData()
|
||
{
|
||
static_print_info("ResendAudioData start");
|
||
|
||
osThreadAttr_t task_attr = {"resend_audio_data_task", 0, NULL, 0, NULL, 0x1000, (osPriority_t)17, 0, 0};
|
||
|
||
resend_audio_data_id = osThreadNew(resend_audio_task, NULL, &task_attr);
|
||
if (resend_audio_data_id == NULL) {
|
||
static_print_error("resend_audio_data_task failed");
|
||
return;
|
||
}
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::RecordDestroy(void)
|
||
{
|
||
static_print_debug("RecordDestroy");
|
||
Audio::AudioManager &amIntance = Audio::AudioManager::GetInstance();
|
||
MediaMutexLock(g_lefun_ai_mutex);
|
||
if (!lefunAiCapturer_->Stop()) {
|
||
static_print_warn("Stop failed");
|
||
}
|
||
if (!lefunAiCapturer_->Release()) {
|
||
static_print_warn("Release failed");
|
||
}
|
||
|
||
delete lefunAiRecordBuffer_;
|
||
lefunAiRecordBuffer_ = nullptr;
|
||
|
||
if (amIntance.DeactivateAudioInterrupt(interrupt_) != 0) {
|
||
static_print_warn("deactivate audio interrupt failed");
|
||
}
|
||
needStop_ = false;
|
||
needPause_ = false;
|
||
lefunAiCapturer_.reset();
|
||
lefunAiCapturer_ = nullptr;
|
||
isActivateAudioInterrupt_ = false;
|
||
recordIsRuning_ = false;
|
||
MediaMutexUnLock(g_lefun_ai_mutex);
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::RequestIntoLefunAi()
|
||
{
|
||
// tjd_lefun_ai_request_into_lefun_ai();
|
||
// tjd_ble_upload_ai_func_attribute();
|
||
tjd_lefun_ai_request_into_play_dial();
|
||
tjd_ble_upload_play_dial_func_attribute((ai_picture_style_t)(TjdUiAppPlayDialModel::GetInstance().dialStyle_), 0,
|
||
0);
|
||
printf("TjdUiAppPlayDialModel::RequestIntoLefunAi() dialStyle_:%d\n", dialStyle_);
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::LefunAiRequestIntoAudioTransmit() { tjd_lefun_ai_request_into_audio_transmit(); }
|
||
|
||
void TjdUiAppPlayDialModel::LefunAiRequestStartAudioTransmit() { tjd_lefun_ai_request_start_audio_transmit(); }
|
||
|
||
void TjdUiAppPlayDialModel::LefunAiRequestExitLefunAi()
|
||
{
|
||
// tjd_lefun_ai_request_exit_lefun_ai();
|
||
tjd_lefun_ai_request_exit_play_dial();
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::LefunAiRequestEndAudioTransmit() { tjd_lefun_ai_request_end_audio_transmit(); }
|
||
|
||
void TjdUiAppPlayDialModel::LefunAiRequestImageData()
|
||
{
|
||
printf("TjdUiAppPlayDialModel::LefunAiRequestImageData()\n");
|
||
tjd_play_dial_request_diagram_generation(0, 0); //流id无效,首次传0。
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::LefunAiRequestTransmitImageData()
|
||
{
|
||
printf("TjdUiAppPlayDialModel::LefunAiRequestTransmitImageData()\n");
|
||
tjd_play_dial_request_diagram_transmit(0); //原图0。
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::TransmitAudioData(uint8_t *data, uint16_t len) { tjd_ble_transmit_audio_data(data, len); }
|
||
|
||
bool TjdUiAppPlayDialModel::GetAiAudioTransFlag() { return tjd_ble_get_ai_audio_trans_flag(); }
|
||
|
||
void TjdUiAppPlayDialModel::lefun_ai_data_callback(struct data_flow *data)
|
||
{
|
||
printf("PlayDialModel data_callback");
|
||
// static_print_info("PlayDialModel data_callback");
|
||
GraphicService::GetInstance()->PostGraphicEvent([&]() {
|
||
if (data == nullptr || data->data == nullptr)
|
||
return;
|
||
data_flow = data;
|
||
|
||
if (data_flow->data_flow_type == 0) {
|
||
story_question_flag = true;
|
||
// TjdUiAppPlayDialModel::question.clear();
|
||
g_PlayDialQuestion = std::string((char *)data_flow->data, data_flow->len);
|
||
// TjdUiAppPlayDialModel::g_PlayDialQuestion(std::string((char *)data_flow->data));
|
||
printf("PlayDialModel data_callback g_PlayDialQuestion:%s\n", g_PlayDialQuestion.c_str());
|
||
} else if (data_flow->data_flow_type == 1) {
|
||
story_answer_flag = true;
|
||
// TjdUiAppPlayDialModel::g_PlayDialAnswer.clear();
|
||
// TjdUiAppPlayDialModel::g_PlayDialAnswer.append(std::string((char *)data_flow->data));
|
||
g_PlayDialAnswer += std::string((char *)data_flow->data);
|
||
}
|
||
|
||
if (story_question_flag && story_answer_flag) {
|
||
// TjdUiAppPlayDialView::GetInstance().ShowView(PLAYDIAL_AI_MAKING);//saimen
|
||
}
|
||
});
|
||
}
|
||
|
||
static OHOS::GraphicTimer *waiterTimer_ = nullptr; // 运动进行中计时器
|
||
std::atomic<bool> PlayDialWaiterTimerStart(false);
|
||
static void WaiterTimerCallback(void *arg)
|
||
{
|
||
printf("PlayDialModel WaiterTimerCallback\n");
|
||
if (PlayDialWaiterTimerStart.load()) {
|
||
printf("PlayDialModel WaiterTimerCallback start\n");
|
||
PlayDialWaiterTimerStart.store(false);
|
||
waiterTimer_->Stop();
|
||
waiterTimer_->Start();
|
||
return;
|
||
}
|
||
PlayDialWaiterTimerStart.store(false);
|
||
|
||
if (data_flow->data != NULL) {
|
||
free(data_flow->data);
|
||
data_flow->data = NULL;
|
||
}
|
||
|
||
data_flow->len = 0;
|
||
data_flow->data_flow_id = 0;
|
||
data_flow->data_flow_type = 0;
|
||
|
||
// 保存历史记录到 JSON 文件
|
||
GraphicService::GetInstance()->PostGraphicEvent([&]() {
|
||
TjdUiAppPlayDialModel::History lefunAiHistory;
|
||
TjdUiAppPlayDialModel::GetInstance().loadFromJson(&lefunAiHistory);
|
||
// printf("line = %d=======lefunAiHistory=%d==========\n", __LINE__, lefunAiHistory.total_records);
|
||
story_answer_flag = false;
|
||
TjdUiAppPlayDialModel::GetInstance().addRecord(&lefunAiHistory, g_PlayDialQuestion, g_PlayDialAnswer);
|
||
// printf("line = %d=========lefunAiHistory=%d==========\n", __LINE__, lefunAiHistory.total_records);
|
||
TjdUiAppPlayDialModel::GetInstance().saveToJson(&lefunAiHistory, LEFUN_AI_STORY_FILE_PATH);
|
||
g_PlayDialQuestion.clear();
|
||
g_PlayDialAnswer.clear();
|
||
});
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::DeleDataFlow(void)
|
||
{
|
||
if (data_flow->data != NULL) {
|
||
free(data_flow->data);
|
||
data_flow->data = NULL;
|
||
}
|
||
|
||
if (data_flow) {
|
||
data_flow->len = 0;
|
||
data_flow->data_flow_id = 0;
|
||
data_flow->data_flow_type = 0;
|
||
}
|
||
|
||
// 保存历史记录到 JSON 文件
|
||
History lefunAiHistory;
|
||
// printf("line = %d==================\n", __LINE__);
|
||
TjdUiAppPlayDialModel::GetInstance().loadFromJson(&lefunAiHistory);
|
||
// printf("line = %d=======lefunAiHistory=%d==========\n", __LINE__, lefunAiHistory.total_records);
|
||
story_answer_flag = false;
|
||
TjdUiAppPlayDialModel::GetInstance().addRecord(&lefunAiHistory, g_PlayDialQuestion, g_PlayDialAnswer);
|
||
// printf("line = %d=========lefunAiHistory=%d==========\n", __LINE__, lefunAiHistory.total_records);
|
||
TjdUiAppPlayDialModel::GetInstance().saveToJson(&lefunAiHistory, LEFUN_AI_STORY_FILE_PATH);
|
||
g_PlayDialQuestion.clear();
|
||
g_PlayDialAnswer.clear();
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::lefun_ai_data_end_callback(struct data_flow *data)
|
||
{
|
||
printf("TjdUiAppPlayDialModel::lefun_ai_data_end_callback()\n");
|
||
TjdUiAppPlayDialView::GetInstance().Result_ = AI_RESULT_SELECT_TXT;
|
||
PlayDialWaiterTimerStart.store(true);
|
||
GraphicService::GetInstance()->PostGraphicEvent([&]() {
|
||
if (data == nullptr || data->data == nullptr) {
|
||
return;
|
||
}
|
||
data_flow = data;
|
||
|
||
if (story_question_flag && story_answer_flag) {
|
||
// TjdUiAppPlayDialView::GetInstance().ShowView(PLAYDIAL_AI_MAKING);//saimen
|
||
}
|
||
|
||
// if (waiterTimer_ == nullptr) {
|
||
// waiterTimer_ = new OHOS::GraphicTimer(5000, WaiterTimerCallback, (void *)data, false);
|
||
// waiterTimer_->Start();
|
||
// }
|
||
});
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::play_dial_app_ready_callback(bool isSuccess)
|
||
{
|
||
printf("TjdUiAppPlayDialModel::play_dial_app_ready_callback():%d\n", isSuccess);
|
||
TjdUiAppPlayDialView::GetInstance().Result_ = AI_RESULT_APP_DONE;
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::play_dial_original_end_callback(char *original_picture_name)
|
||
{
|
||
TjdUiAppPlayDialView::GetInstance().Result_ = AI_RESULT_SELECT_IMG;
|
||
g_PlayDialPath = std::string(original_picture_name);
|
||
printf("TjdUiAppPlayDialModel::play_dial_original_end_callback():%s-%s\n", original_picture_name,
|
||
g_PlayDialPath.c_str());
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::RegisterLefunAiDataCallback(lefun_ai_data_callback_t callback)
|
||
{
|
||
register_lefun_ai_data_callback(callback);
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::UnregisterLefunAiDataCallback() { unregister_lefun_ai_data_callback(); }
|
||
|
||
void TjdUiAppPlayDialModel::RegisterLefunAiDataEndCallback(lefun_ai_data_callback_t callback)
|
||
{
|
||
register_lefun_ai_data_end_callback(callback);
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::UnregisterLefunAiDataEndCallback() { unregister_lefun_ai_data_end_callback(); }
|
||
|
||
void TjdUiAppPlayDialModel::RegisterAppImageReadyCallback(play_dial_request_generate_picture_callback_t callback)
|
||
{
|
||
register_play_dial_request_generate_picture_callback(callback);
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::UnregisterAppImageReadyCallback()
|
||
{
|
||
unregister_play_dial_request_generate_picture_callback();
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::RegisterOriginalImageEndCallback(play_dial_original_callback_t callback)
|
||
{
|
||
register_play_dial_original_callback(callback);
|
||
}
|
||
|
||
void TjdUiAppPlayDialModel::UnregisterOriginalImageEndCallback() { unregister_play_dial_original_callback(); }
|
||
|
||
} // namespace TJD
|