#include "./includes/lrc.h" #include "./includes/console.h" #include "./includes/time_delay.h" FILE *open_lrc_file(const char *lrc_path) { FILE *fp = fopen(lrc_path, "rb"); // 以二进制只读方式打开歌词文件 if (fp == NULL) // 如果打开歌词文件失败 { printf("歌词打开失败\n"); perror("fopen"); return NULL; // 打开歌词文件失败,退出程序 } return fp; } long get_lrc_size(FILE *fp) { fseek(fp, 0, SEEK_END); // fseek()将文件指针移动到文件末尾 long size = ftell(fp); // ftell()返回当前文件指针位置, 即文件大小 rewind(fp); // rewind()将文件指针移动到文件开头 return size; } char *get_lrc_mem_data(FILE *fp) { long lrc_size = get_lrc_size(fp); // 获取歌词文件大小 char *lrc_mem_data = (char *)calloc(1, lrc_size + 1); // 为歌词数据分配内存,+1 是为了保存最后位置结束符 if (NULL == lrc_mem_data) { printf("歌词数据分配内存失败\n"); perror("malloc"); fclose(fp); // 关闭歌词文件 return NULL; // 为歌词数据分配内存失败,退出程序 } fread(lrc_mem_data, lrc_size, 1, fp); // 读入歌词数据 fclose(fp); // 关闭歌词文件 return lrc_mem_data; // 返回歌词数据 } LRC *insert_lrc_node(LRC *head, LRC new_node) { LRC *p = head; // 定义临时指针变量 if (p == NULL) // 如果链表为空 { head = (LRC *)malloc(sizeof(LRC)); // 为链表分配内存 if (head == NULL) // 如果分配内存失败 { printf("歌词节点分配内存失败\n"); // 打印错误信息 perror("malloc"); // 打印错误信息 return NULL; // 返回 NULL } head->time = new_node.time; // 将新节点的时间点赋值给链表头节点 strcpy(head->lrc_buf, new_node.lrc_buf); // 将新节点的歌词内容赋值给链表头节点 head->next = NULL; // 将链表头节点的 next 指针域置为 NULL return head; // 返回链表头节点 } while (p->next != NULL) // 遍历链表,找到链表尾节点 { p = p->next; // 指向下一个节点 } p->next = (LRC *)malloc(sizeof(LRC)); // 为链表尾节点的 next 指针域分配内存 if (p->next == NULL) // 如果分配内存失败 { printf("歌词节点分配内存失败\n"); // 打印错误信息 perror("malloc"); // 打印错误信息 return NULL; // 返回 NULL } p->next->time = new_node.time; // 将新节点的时间点赋值给链表尾节点 strcpy(p->next->lrc_buf, new_node.lrc_buf); // 将新节点的歌词内容赋值给链表尾节点 p->next->next = NULL; // 将链表尾节点的 next 指针域置为 NULL return head; // 返回链表头节点 } LRC *search_lrc_node(LRC *head, int time) { LRC *p = head; // 定义临时指针变量 while (p != NULL) // 遍历链表 { if (p->time == time) // 找到指定时间点的歌词 { return p; // 返回歌词节点 } p = p->next; // 指向下一个节点 } return NULL; // 没有找到指定时间点的歌词 } void handle_first_4_lines(char *lrc_lines[]) { clear_screen(); // 清屏 cusor_hide(); // 隐藏光标 for (int i = 0; i < 4; i++) { char tmp[200] = ""; // 定义临时字符串 sscanf(lrc_lines[i], "%*[^:]:%[^]]", tmp); // 从歌词行中提取时间信息, "%*[^:]:%[^]]" 表示跳过第一个冒号,然后读取到第一个右中括号为止的内容 switch (i) { case 0: cusor_moveto(25, 2); // 将光标移动到指定位置 printf("歌曲: %s\n", tmp); break; case 1: cusor_moveto(25, 3); // 将光标移动到指定位置 printf("歌手: %s\n", tmp); break; case 2: cusor_moveto(25, 4); // 将光标移动到指定位置 printf("专辑: %s\n", tmp); break; case 3: cusor_moveto(25, 5); // 将光标移动到指定位置 printf("制作: %s\n", tmp); break; } } } // 保存到链表 LRC *save_to_linklist(char *lrc_lines[], int rows) { // 逐行分析剩下的歌词,将时间、歌词内容保存到链表中 LRC *head = NULL; // 定义歌词链表头指针 for (int i = 4; i < rows; i++) { char *str_lrc = lrc_lines[i]; // 获取歌词行 while (*str_lrc == '[') // 每当遇到'['时,就跳过 str_lrc += 10; // 跳过前面的时间标签 char *str_time = lrc_lines[i]; // 获取歌词行 while (*str_time == '[') { //秒级计数 int m = 0, s = 0; // 定义分钟和秒数 sscanf(str_time, "[%d:%d.%*d]", &m, &s); // 从歌词行中提取时间信息 int time = m * 60 + s; // 计算歌词时间点 // // 毫秒级计数 (这里有问题,待解决) // int m = 0, s = 0, ms = 0; // 定义分钟和秒数和毫秒数 // sscanf(str_time, "[%d:%d.%d]", &m, &s, &ms); // 从歌词行中提取时间信息 // int time = (m * 60 + s) * 1000 + ms; // 计算歌词时间点: 毫秒数转换为秒数, 1000ms = 1s LRC tmp; tmp.time = time; // 保存歌词时间点 strcpy(tmp.lrc_buf, str_lrc); // 保存歌词内容 tmp.lrc_cur_num = i; // 保存当前歌词行数 head = insert_lrc_node(head, tmp); // 将歌词节点插入到链表中 str_time += 10; // 跳过前面的时间标签 } } return head; // 返回链表头指针 } void show_lrcs(LRC *head) { // 逐行打印歌词 int t = 0; // long t = 0; // 用于毫秒级延时 char buf1[200] = ""; char buf2[200] = ""; char buf3[200] = ""; char buf4[200] = ""; while (1) { set_fg_color(COLOR_BLUE); // 设置前景颜色为红色 cusor_moveto(29, 7); // 将光标移动到指定位置 printf("%02d:%02d", t / 60, t % 60); // 打印歌曲播放时间 // 打印歌曲播放时间 (毫秒级延时) // printf("%02d:%02d", t / (60 * 1000), (t / 1000) % 60); fflush(stdout); // 刷新输出缓冲区,立即输出 LRC *ret = search_lrc_node(head, t); // 查找歌词节点 if (ret != NULL) // 如果找到歌词节点 { // 用于歌词滚动轮换 strcpy(buf1, buf2); // 将buf2中的内容复制到buf1中 strcpy(buf2, buf3); // 将buf3中的内容复制到buf2中 strcpy(buf3, buf4); // 将buf4中的内容复制到buf3中 strcpy(buf4, ret->lrc_buf); // 将歌词内容复制到buf4中 cusor_moveto(17, 9); // 将光标移动到指定位置 printf("\033[K"); // 清除从光标到行尾的内容 printf("%s", buf1); // 打印歌词内容 cusor_moveto(17, 10); // 将光标移动到指定位置 printf("\033[K"); // 清除从光标到行尾的内容 printf("%s", buf2); // 打印歌词内容 cusor_moveto(17, 11); // 将光标移动到指定位置 printf("\033[K"); // 清除从光标到行尾的内容 printf("%s", buf3); // 打印歌词内容 set_fg_color(COLOR_RED); // 设置红色前景色 cusor_moveto(17, 12); // 将光标移动到指定位置 printf("\033[K"); // 清除从光标到行尾的内容 printf("%s", buf4); // 打印歌词内容 set_fg_color(COLOR_BLUE); fflush(stdout); // 刷新输出缓冲区,立即输出 } time_delay(1); // 延时1秒 t++; // 时间加1 // delay_ms(1); // 延时1毫秒 (毫秒级延时) // t += 1000; // 时间加1 (1毫秒) } }