diff --git a/lyric_analysis/srcs/console.c b/lyric_analysis/srcs/console.c index 7647d51..011b800 100644 --- a/lyric_analysis/srcs/console.c +++ b/lyric_analysis/srcs/console.c @@ -2,33 +2,33 @@ #include #include "./includes/console.h" -void cusor_moveto(int x, int y) -{ // ESC[y;xH - printf("\033[%d;%dH", y, x); - fflush(stdout); +void cusor_moveto(int x, int y) //将光标移动到指定位置 +{ // ESC[y;xH + printf("\033[%d;%dH", y, x); // 将光标移动到指定位置,x 为横坐标,y 为纵坐标,\033 为 ESC + fflush(stdout); // 刷新输出缓冲区,立即输出 } //保存光标位置 void cusor_get_pos(void) -{ // ESC[s - printf("\033[s"); +{ // ESC[s + printf("\033[s"); // 保存光标位置 fflush(stdout); } //恢复光标位置 void cusor_set_pos(void) -{ // ESC[u - printf("\033[u"); +{ // ESC[u + printf("\033[u"); // 恢复光标位置 fflush(stdout); } void cusor_hide(void) { - printf("\033[?25l"); + printf("\033[?25l"); // 隐藏光标 } //清屏 void clear_screen(void) -{ // ESC[2J - printf("\033[2J"); +{ // ESC[2J + printf("\033[2J"); // 清屏 fflush(stdout); } diff --git a/lyric_analysis/srcs/includes/lrc.h b/lyric_analysis/srcs/includes/lrc.h index f5a6439..301587a 100644 --- a/lyric_analysis/srcs/includes/lrc.h +++ b/lyric_analysis/srcs/includes/lrc.h @@ -7,13 +7,19 @@ typedef struct lrc { - int time; // 歌词时间点 + int time; // 歌词时间点 + // long time; // 用于毫秒级延时 char lrc_buf[200]; // 歌词内容 int lrc_cur_num; // 当前歌词行数, 用于标记当前歌词(第几句歌词 从1开始) - struct lrc *next; // 指向下一行歌词(指向链表下一个节点) + + struct lrc *next; // 指向下一行歌词(指向链表下一个节点) } LRC; -FILE *open_lrc_file(const char *lrc_path); // 打开歌词文件 -long get_lrc_size(FILE *fp); // 获取歌词文件大小 +FILE *open_lrc_file(const char *lrc_path); // 打开歌词文件 +long get_lrc_size(FILE *fp); // 获取歌词文件大小 +char *get_lrc_mem_data(FILE *fp); // 获取歌词文件内容 +LRC *insert_lrc_node(LRC *head, LRC new_node); // 插入歌词节点 (尾插法) +LRC *search_lrc_node(LRC *head, int time); // 查找歌词节点 (根据时间点查找歌词节点) +// LRC *search_lrc_node(LRC *head, long time); // 查找歌词节点 (根据时间点查找歌词节点) (用于毫秒级延时) #endif \ No newline at end of file diff --git a/lyric_analysis/srcs/includes/time_delay.h b/lyric_analysis/srcs/includes/time_delay.h index 726e006..7fb55dc 100644 --- a/lyric_analysis/srcs/includes/time_delay.h +++ b/lyric_analysis/srcs/includes/time_delay.h @@ -2,6 +2,7 @@ #define __TIME_DELAY_H__ // 自封装延时函数 -void time_delay(int sec); +void time_delay(int sec); // 延时指定秒数 +void delay_ms(int milliseconds); // 延迟指定毫秒数 #endif \ No newline at end of file diff --git a/lyric_analysis/srcs/lrc.c b/lyric_analysis/srcs/lrc.c index 4422ae9..3e461aa 100644 --- a/lyric_analysis/srcs/lrc.c +++ b/lyric_analysis/srcs/lrc.c @@ -2,10 +2,12 @@ FILE *open_lrc_file(const char *lrc_path) { - FILE *fp = fopen(lrc_path, "rb"); - if (fp == NULL) + FILE *fp = fopen(lrc_path, "rb"); // 以二进制只读方式打开歌词文件 + if (fp == NULL) // 如果打开歌词文件失败 { - return NULL; + printf("歌词打开失败\n"); + perror("fopen"); + return NULL; // 打开歌词文件失败,退出程序 } return fp; } @@ -15,6 +17,70 @@ long get_lrc_size(FILE *fp) fseek(fp, 0, SEEK_END); // fseek()将文件指针移动到文件末尾 long size = ftell(fp); // ftell()返回当前文件指针位置, 即文件大小 rewind(fp); // rewind()将文件指针移动到文件开头 - printf("歌词大小为: %ld Byte\n", size); + 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; // 没有找到指定时间点的歌词 } \ No newline at end of file diff --git a/lyric_analysis/srcs/main.c b/lyric_analysis/srcs/main.c index 6436d18..65cb523 100644 --- a/lyric_analysis/srcs/main.c +++ b/lyric_analysis/srcs/main.c @@ -9,45 +9,142 @@ int main() const char *song_path = "./songs/简单爱.mp3"; const char *lrc_path = "./lrcs/简单爱.lrc"; - FILE *fp = open_lrc_file(lrc_path); // 打开歌词文件 - if (NULL == fp) - { - printf("歌词打开失败\n"); - perror("fopen"); - return -1; // 打开歌词文件失败,退出程序 - } - mplayer_play(song_path); // 启动mplayer播放器,播放歌曲 + FILE *lrc_fp = open_lrc_file(lrc_path); // 打开歌词文件 - long lrc_size = get_lrc_size(fp); // 获取并打印歌词文件大小 + long lrc_size = get_lrc_size(lrc_fp); // 获取并打印歌词文件大小 + // printf("歌词大小为: %ld Byte\n", lrc_size); - char *lrc_mem_data = (char *)malloc(lrc_size); // 为歌词数据分配内存 - if (NULL == lrc_mem_data) - { - printf("歌词数据分配内存失败\n"); - perror("malloc"); - return -1; // 为歌词数据分配内存失败,退出程序 - } + char *lrc_mem_data = get_lrc_mem_data(lrc_fp); // 获取歌词数据 - // 读入歌词内容 - fread(lrc_mem_data, lrc_size, 1, fp); // 读入歌词数据 - // printf("%s", lrc_mem_data); - // FILE *fwx = fopen("../lrc/简单爱copy.lrc", "w"); - // fwrite(lrc_mem_data, lrc_size, 1, fwx); + // mplayer_play(song_path); // 逐行解析歌词 - char *line = strtok(lrc_mem_data, "\r\n"); // 逐行解析歌词 - int i = 1; - while (line != NULL) + char *lrc_lines[200] = {NULL}; // 定义歌词行指针数组 + int i = 0; // 定义歌词行数 + lrc_lines[i] = strtok(lrc_mem_data, "\r\n"); // 将歌词数据按行分割 + while (lrc_lines[i] != NULL) { - time_delay(2); - printf("%d.\t%s\n", i++, line); - line = strtok(NULL, "\r\n"); + // 打印歌词 + // time_delay(1); // 延时1秒 + // printf("%d.\t%s\n", i, lrc_lines[i]); // 打印歌词 + i++; // 行数加1 + lrc_lines[i] = strtok(NULL, "\r\n"); // 将剩余行歌词数据按行分割 + } + + // 分析前 4 行歌词 + int rows = i; // 获取歌词总行数 + + 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; + default: + break; + } + } + + // 逐行分析剩下的歌词,将时间、歌词内容保存到链表中 + 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; // 跳过前面的时间标签 + } + } + + // 开始播放歌曲 + mplayer_play(song_path); // 启动mplayer播放器,播放歌曲 + + // 逐行打印歌词 + 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毫秒) } - // FILE *fwx = fopen("../lrc/简单爱copy.lrc", "wb"); - // fwrite(lrc_mem_data, lrc_size, 1, fwx); // 释放内存,关闭文件 free(lrc_mem_data); // 释放歌词数据内存 - fclose(fp); // 关闭歌词文件 return 0; } \ No newline at end of file diff --git a/lyric_analysis/srcs/time_delay.c b/lyric_analysis/srcs/time_delay.c index d6039f3..71b6b24 100644 --- a/lyric_analysis/srcs/time_delay.c +++ b/lyric_analysis/srcs/time_delay.c @@ -1,9 +1,10 @@ +#include // usleep() #include #include #include "includes/time_delay.h" void time_delay(int sec) { - int s_time, e_time; + int s_time, e_time; // 定义开始时间和结束时间,单位为秒 s_time = time(NULL); while (1) { @@ -12,6 +13,14 @@ void time_delay(int sec) break; } } + +// 延迟指定毫秒数 +void delay_ms(int milliseconds) +{ + usleep(milliseconds * 1000); // usleep()函数用于毫秒级延迟 +} + +// 测试用例 // int main(int argc, char *argv[]) // { // while(1)