增加景点长播放语音功能
This commit is contained in:
parent
bf8816ac83
commit
953f3187df
|
|
@ -28,18 +28,31 @@
|
||||||
#define PLAY_DISTANCE_THRESHOLD 50.0 // 有效播报距离(米)
|
#define PLAY_DISTANCE_THRESHOLD 50.0 // 有效播报距离(米)
|
||||||
#define DISTANCE_CHANGE_THRESHOLD 10.0 // 距离变化阈值(米)
|
#define DISTANCE_CHANGE_THRESHOLD 10.0 // 距离变化阈值(米)
|
||||||
#define TTS_PRIORITY 5 // TTS播报优先级
|
#define TTS_PRIORITY 5 // TTS播报优先级
|
||||||
|
#define MAX_TTS_SEGMENT_LEN 50 // TTS每段最大长度(字符)
|
||||||
|
|
||||||
|
|
||||||
static nmeaPARSER parser;
|
static nmeaPARSER parser;
|
||||||
|
|
||||||
//该版本仅实现基本的靠近播报功能,长文字播放功能待开发
|
//该版本仅实现基本的靠近播报功能,长文字播放功能待开发
|
||||||
|
|
||||||
|
|
||||||
// 全局景点链表
|
// 文本分段结构 [新增]
|
||||||
|
typedef struct {
|
||||||
|
char** segments; // 分段文本数组
|
||||||
|
int count; // 分段数量
|
||||||
|
} SegmentedText;
|
||||||
|
|
||||||
|
|
||||||
|
// 修改景点节点结构 [修改]
|
||||||
typedef struct AttractionNode {
|
typedef struct AttractionNode {
|
||||||
Attraction attraction;
|
double longitude;
|
||||||
|
double latitude;
|
||||||
|
char name[50];
|
||||||
|
SegmentedText description; // 从char[]改为SegmentedText结构 [关键修改]
|
||||||
struct AttractionNode* next;
|
struct AttractionNode* next;
|
||||||
} AttractionNode;
|
} AttractionNode;
|
||||||
|
|
||||||
|
|
||||||
// 播报状态
|
// 播报状态
|
||||||
typedef struct {
|
typedef struct {
|
||||||
AttractionNode* last_broadcast;
|
AttractionNode* last_broadcast;
|
||||||
|
|
@ -76,6 +89,48 @@ static int tts_volume = 10;
|
||||||
static osThreadId_t Attr_Broadcast_ThreadId = NULL; //串口数据接收、解析任务Handle
|
static osThreadId_t Attr_Broadcast_ThreadId = NULL; //串口数据接收、解析任务Handle
|
||||||
|
|
||||||
|
|
||||||
|
// 智能分段函数
|
||||||
|
SegmentedText smart_segment_text(const char* text, int max_segment_len) {
|
||||||
|
SegmentedText result = {0};
|
||||||
|
if (!text || *text == '\0') return result;
|
||||||
|
|
||||||
|
int text_len = strlen(text);
|
||||||
|
int max_segments = (text_len / max_segment_len) + 2;
|
||||||
|
result.segments = cm_malloc(max_segments * sizeof(char*));
|
||||||
|
|
||||||
|
int start = 0;
|
||||||
|
int segment_count = 0;
|
||||||
|
|
||||||
|
while (start < text_len && segment_count < max_segments) {
|
||||||
|
int end = start + max_segment_len;
|
||||||
|
if (end > text_len) end = text_len;
|
||||||
|
|
||||||
|
if (end < text_len) {
|
||||||
|
// 寻找最佳分割点(标点符号优先)
|
||||||
|
int best_break = end;
|
||||||
|
for (int i = end; i > start; i--) {
|
||||||
|
if (strchr("。!?,;.!?", text[i])) {
|
||||||
|
best_break = i + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end = best_break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建分段
|
||||||
|
int seg_len = end - start;
|
||||||
|
result.segments[segment_count] = cm_malloc(seg_len + 1);
|
||||||
|
strncpy(result.segments[segment_count], text + start, seg_len);
|
||||||
|
result.segments[segment_count][seg_len] = '\0';
|
||||||
|
segment_count++;
|
||||||
|
|
||||||
|
start = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.count = segment_count;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//多文字tts,景区播报专用
|
//多文字tts,景区播报专用
|
||||||
|
|
@ -92,6 +147,18 @@ void safe_tts_play(const char* segments[], int count) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 释放分段内存 [新增]
|
||||||
|
void free_segmented_text(SegmentedText* st) {
|
||||||
|
if (st && st->segments) {
|
||||||
|
for (int i = 0; i < st->count; i++) {
|
||||||
|
cm_free(st->segments[i]);
|
||||||
|
}
|
||||||
|
cm_free(st->segments);
|
||||||
|
st->segments = NULL;
|
||||||
|
st->count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//计算到景点的距离
|
//计算到景点的距离
|
||||||
double location_service_calculate_distance(Location loc1, Location loc2) {
|
double location_service_calculate_distance(Location loc1, Location loc2) {
|
||||||
|
|
@ -141,11 +208,13 @@ void attr_broadcast_add_attraction(double lon, double lat,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 填充景点信息
|
// 填充景点信息
|
||||||
new_node->attraction.longitude = lon;
|
new_node->longitude = lon;
|
||||||
new_node->attraction.latitude = lat;
|
new_node->latitude = lat;
|
||||||
strncpy(new_node->attraction.name, name, sizeof(new_node->attraction.name)-1);
|
strncpy(new_node->name, name, sizeof(new_node->name)-1);
|
||||||
strncpy(new_node->attraction.description, desc, sizeof(new_node->attraction.description)-1);
|
new_node->name[sizeof(new_node->name)-1] = '\0';
|
||||||
|
|
||||||
|
new_node->description = smart_segment_text(desc, MAX_TTS_SEGMENT_LEN);
|
||||||
|
|
||||||
// 添加到链表头部
|
// 添加到链表头部
|
||||||
new_node->next = attractions_head;
|
new_node->next = attractions_head;
|
||||||
attractions_head = new_node;
|
attractions_head = new_node;
|
||||||
|
|
@ -154,6 +223,26 @@ void attr_broadcast_add_attraction(double lon, double lat,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 释放所有景点内存 最新添加
|
||||||
|
void attr_broadcast_free_all() {
|
||||||
|
if (attractions_mutex == NULL) return;
|
||||||
|
|
||||||
|
osMutexAcquire(attractions_mutex, osWaitForever);
|
||||||
|
|
||||||
|
AttractionNode* current = attractions_head;
|
||||||
|
while (current) {
|
||||||
|
AttractionNode* next = current->next;
|
||||||
|
free_segmented_text(¤t->description);
|
||||||
|
cm_free(current);
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
attractions_head = NULL;
|
||||||
|
osMutexRelease(attractions_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 设置播报距离阈值 (米)
|
// 设置播报距离阈值 (米)
|
||||||
void attr_broadcast_set_distance_threshold(double threshold) {
|
void attr_broadcast_set_distance_threshold(double threshold) {
|
||||||
if (threshold > 0) {
|
if (threshold > 0) {
|
||||||
|
|
@ -171,13 +260,13 @@ static AttractionNode* find_nearest_attraction(Location current_pos) {
|
||||||
AttractionNode* current = attractions_head;
|
AttractionNode* current = attractions_head;
|
||||||
AttractionNode* nearest = attractions_head;
|
AttractionNode* nearest = attractions_head;
|
||||||
double min_distance = location_service_calculate_distance(
|
double min_distance = location_service_calculate_distance(
|
||||||
(Location){current->attraction.longitude, current->attraction.latitude},
|
(Location){current->longitude, current->latitude},
|
||||||
current_pos
|
current_pos
|
||||||
);
|
);
|
||||||
|
|
||||||
while (current != NULL) {
|
while (current != NULL) {
|
||||||
double distance = location_service_calculate_distance(
|
double distance = location_service_calculate_distance(
|
||||||
(Location){current->attraction.longitude, current->attraction.latitude},
|
(Location){current->longitude, current->latitude},
|
||||||
current_pos
|
current_pos
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -197,25 +286,34 @@ static AttractionNode* find_nearest_attraction(Location current_pos) {
|
||||||
static void play_attraction_info(AttractionNode* attraction, double distance) {
|
static void play_attraction_info(AttractionNode* attraction, double distance) {
|
||||||
if (!attraction) return;
|
if (!attraction) return;
|
||||||
|
|
||||||
// 构建播报文本
|
// 构建距离提示
|
||||||
char buffer[300];
|
char distance_msg[100];
|
||||||
snprintf(buffer, sizeof(buffer), "您已到达%s,%s。距离%.1f米。",
|
snprintf(distance_msg, sizeof(distance_msg), "您已到达%s,距离%.1f米。",
|
||||||
attraction->attraction.name,
|
attraction->name, distance);
|
||||||
attraction->attraction.description,
|
|
||||||
distance);
|
|
||||||
|
|
||||||
|
|
||||||
broadcast_state.is_playing = 1;
|
|
||||||
// 调用TTS接口播放文本
|
|
||||||
local_tts_text_play(buffer, 0, 1);
|
|
||||||
|
|
||||||
//等待
|
// 创建播放数组(距离提示 + 所有描述分段)
|
||||||
while(local_tts_get_play_state() != 0) {
|
int total_segments = attraction->description.count + 1;
|
||||||
osDelay(100);
|
const char** segments = cm_malloc(total_segments * sizeof(char*));
|
||||||
|
if (!segments) return;
|
||||||
|
|
||||||
|
segments[0] = distance_msg;
|
||||||
|
for (int i = 0; i < attraction->description.count; i++) {
|
||||||
|
segments[i+1] = attraction->description.segments[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
broadcast_state.is_playing = 1;
|
||||||
|
DEBUG("Playing attraction info: %s (%d segments)\n",
|
||||||
|
attraction->name, total_segments);
|
||||||
|
|
||||||
|
// 播放所有分段
|
||||||
|
safe_tts_play(segments, total_segments);
|
||||||
|
|
||||||
|
cm_free(segments);
|
||||||
|
|
||||||
// 更新播报状态
|
// 更新播报状态
|
||||||
broadcast_state.last_broadcast = attraction;
|
broadcast_state.last_broadcast = attraction;
|
||||||
broadcast_state.last_distance = distance;
|
broadcast_state.last_distance = distance;
|
||||||
|
broadcast_state.is_playing = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -247,7 +345,7 @@ static void attr_broadcast_task(void* arg) {
|
||||||
if (nearest != NULL) {
|
if (nearest != NULL) {
|
||||||
// 计算距离
|
// 计算距离
|
||||||
double distance = location_service_calculate_distance(
|
double distance = location_service_calculate_distance(
|
||||||
(Location){nearest->attraction.longitude, nearest->attraction.latitude},
|
(Location){nearest->longitude, nearest->latitude},
|
||||||
current_pos
|
current_pos
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -314,7 +412,7 @@ void attr_broadcast_init(void) {
|
||||||
|
|
||||||
osThreadAttr_t attr_broadcast_task_attr = {0};
|
osThreadAttr_t attr_broadcast_task_attr = {0};
|
||||||
attr_broadcast_task_attr.name = "attr_broadcast_task";
|
attr_broadcast_task_attr.name = "attr_broadcast_task";
|
||||||
attr_broadcast_task_attr.stack_size = 4096 * 5;
|
attr_broadcast_task_attr.stack_size = 4096 * 8;
|
||||||
attr_broadcast_task_attr.priority= osPriorityNormal;
|
attr_broadcast_task_attr.priority= osPriorityNormal;
|
||||||
|
|
||||||
Attr_Broadcast_ThreadId= osThreadNew(attr_broadcast_task, 0, &attr_broadcast_task_attr);
|
Attr_Broadcast_ThreadId= osThreadNew(attr_broadcast_task, 0, &attr_broadcast_task_attr);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue