Compare commits

..

7 Commits

19 changed files with 346 additions and 68 deletions
Binary file not shown.
+34
View File
@@ -0,0 +1,34 @@
# 定义路径变量
SRC_DIR = srcs
OBJ_DIR = objs
BIN_DIR = ./
# 定义编译器
CC = gcc
# 定义目标文件
TARGET = $(BIN_DIR)/debug
# 定义源文件
SRCS = $(wildcard $(SRC_DIR)/*.c)
# OBJS = $(OBJ_DIR)/lrc.o $(OBJ_DIR)/console.o $(OBJ_DIR)/start_mplayer.o $(OBJ_DIR)/time_delay.o $(OBJ_DIR)/main.o
OBJS = $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRCS))
# 编译规则
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
$(CC) -c $< -o $@
# 链接规则
$(TARGET): $(OBJS)
$(CC) $^ -o $@
# 默认构建目标
all: $(TARGET)
# 创建目录
$(shell mkdir -p $(OBJ_DIR) $(BIN_DIR))
# 清理规则
clean:
rm -rf $(OBJ_DIR)/*.o $(TARGET)
# 伪目标
.PHONY: all clean
Binary file not shown.
View File
+30
View File
@@ -0,0 +1,30 @@
[ti:简单爱]
[ar:周杰伦]
[al:范特西]
[by:大脸猫]
[00:04.41]周杰伦 Chow, Jay Jou
[00:10.56]简单爱(台视Star blue蓝星主题曲)
[00:18.48]词:徐若瑄 曲:周杰伦
[00:26.44]说不上为什么 我变得很主动
[00:31.37]若爱上一个人 什么都会值得去做
[02:04.94][00:36.09]我想大声宣布 对你依依不舍
[02:09.97][00:41.26]连隔壁邻居都猜到我现在的感受
[02:14.94][00:46.17]河边的风 在吹着头发 飘动
[02:19.80][00:51.25]牵着你的手 一阵莫名感动
[02:24.61][00:55.86]我想带你 回我的外婆家
[02:28.32][00:59.79]一起看着日落 一直到我们都睡着
[03:34.64][02:34.71][01:05.83]我想就这样牵着你的手不放开
[03:39.68][02:39.34][01:10.71]爱能不能够永远单纯没有悲哀
[03:44.27][02:43.90][01:15.44]我想 带你骑单车
[03:46.74][02:46.60][01:18.05]我想 和你看棒球
[03:49.77][02:49.58][01:20.71]想这样没担忧
[03:51.61][02:51.59][01:22.69]唱着歌 一直走☆
[03:54.38][02:54.35][01:25.57]我想就这样牵着你的手不放开
[03:59.19][02:59.01][01:30.41]爱可不可以简简单单没有伤害
[04:03.77][03:03.73][01:35.04]你 靠着我的肩膀
[04:06.33][03:06.26][01:37.49]你 在我胸口睡着
[04:09.13][03:09.34][01:40.57]像这样的生活
[04:11.36][03:11.26][01:42.66]我爱你 你爱我★
[03:13.76][01:44.97]想~~~ 简!简!单!单! 爱~~~
[03:23.61][01:54.30]想~~~ 简!简!单!单! 爱~~~
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-19
View File
@@ -1,19 +0,0 @@
# SRC_DIR := ./srcs
OBJ_DIR := ../objs
BIN_DIR := ../bin
CC = gcc
TARGET = $(BIN_DIR)/main
# SRC = $(SRC_DIR)/lrc.c $(SRC_DIR)/console.c $(SRC_DIR)/start_mplayer.c $(SRC_DIR)/main.c
OBJ = $(OBJ_DIR)/lrc.o $(OBJ_DIR)/console.o $(OBJ_DIR)/start_mplayer.o $(OBJ_DIR)/main.o
# $(OBJ_DIR)/%.o: %.c
# $(CC) -c $< -o $@
$(OBJ_DIR)/%.o: %.c
$(CC) -c $< -o $@
$(TARGET): $(OBJ)
$(CC) $^ -o $@
clean:
rm -rf $(OBJ_DIR)/*.o $(TARGET)
Executable → Regular
+11 -11
View File
@@ -2,33 +2,33 @@
#include <stdlib.h> #include <stdlib.h>
#include "./includes/console.h" #include "./includes/console.h"
void cusor_moveto(int x, int y) void cusor_moveto(int x, int y) //将光标移动到指定位置
{ // ESC[y;xH { // ESC[y;xH
printf("\033[%d;%dH", y, x); printf("\033[%d;%dH", y, x); // 将光标移动到指定位置,x 为横坐标,y 为纵坐标,\033 为 ESC
fflush(stdout); fflush(stdout); // 刷新输出缓冲区,立即输出
} }
//保存光标位置 //保存光标位置
void cusor_get_pos(void) void cusor_get_pos(void)
{ // ESC[s { // ESC[s
printf("\033[s"); printf("\033[s"); // 保存光标位置
fflush(stdout); fflush(stdout);
} }
//恢复光标位置 //恢复光标位置
void cusor_set_pos(void) void cusor_set_pos(void)
{ // ESC[u { // ESC[u
printf("\033[u"); printf("\033[u"); // 恢复光标位置
fflush(stdout); fflush(stdout);
} }
void cusor_hide(void) void cusor_hide(void)
{ {
printf("\033[?25l"); printf("\033[?25l"); // 隐藏光标
} }
//清屏 //清屏
void clear_screen(void) void clear_screen(void)
{ // ESC[2J { // ESC[2J
printf("\033[2J"); printf("\033[2J"); // 清屏
fflush(stdout); fflush(stdout);
} }
View File
+19 -2
View File
@@ -5,7 +5,24 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
FILE *open_lrc_file(const char *lrc_path); // 打开歌词文件 typedef struct lrc
long get_lrc_size(FILE *fp); // 获取歌词文件大小 {
int time; // 歌词时间点
// long time; // 用于毫秒级延时
char lrc_buf[200]; // 歌词内容
int lrc_cur_num; // 当前歌词行数, 用于标记当前歌词(第几句歌词 从1开始)
struct lrc *next; // 指向下一行歌词(指向链表下一个节点)
} LRC;
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); // 查找歌词节点 (根据时间点查找歌词节点) (用于毫秒级延时)
void handle_first_4_lines(char *lrc_lines[]); // 处理前 4 行歌词
LRC *save_to_linklist(char *lrc_lines[], int rows); // 保存到链表
void show_lrcs(LRC *head); // 显示歌词
#endif #endif
View File
@@ -0,0 +1,8 @@
#ifndef __TIME_DELAY_H__
#define __TIME_DELAY_H__
// 自封装延时函数
void time_delay(int sec); // 延时指定秒数
void delay_ms(int milliseconds); // 延迟指定毫秒数
#endif
+189 -4
View File
@@ -1,11 +1,15 @@
#include "./includes/lrc.h" #include "./includes/lrc.h"
#include "./includes/console.h"
#include "./includes/time_delay.h"
FILE *open_lrc_file(const char *lrc_path) FILE *open_lrc_file(const char *lrc_path)
{ {
FILE *fp = fopen(lrc_path, "rb"); FILE *fp = fopen(lrc_path, "rb"); // 以二进制只读方式打开歌词文件
if (fp == NULL) if (fp == NULL) // 如果打开歌词文件失败
{ {
return NULL; printf("歌词打开失败\n");
perror("fopen");
return NULL; // 打开歌词文件失败,退出程序
} }
return fp; return fp;
} }
@@ -15,6 +19,187 @@ long get_lrc_size(FILE *fp)
fseek(fp, 0, SEEK_END); // fseek()将文件指针移动到文件末尾 fseek(fp, 0, SEEK_END); // fseek()将文件指针移动到文件末尾
long size = ftell(fp); // ftell()返回当前文件指针位置, 即文件大小 long size = ftell(fp); // ftell()返回当前文件指针位置, 即文件大小
rewind(fp); // rewind()将文件指针移动到文件开头 rewind(fp); // rewind()将文件指针移动到文件开头
printf("歌词大小为: %ld Byte\n", size);
return 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; // 没有找到指定时间点的歌词
}
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毫秒)
}
} }
+34 -17
View File
@@ -1,31 +1,48 @@
#include "./includes/lrc.h" #include "./includes/lrc.h"
#include "./includes/console.h" #include "./includes/console.h"
#include "./includes/start_mplayer.h" #include "./includes/start_mplayer.h"
#include "includes/time_delay.h"
int main() int main()
{ {
// 定义歌曲和歌词文件路径 // 定义歌曲和歌词文件路径
const char *song_path = "../songs/简单爱.mp3"; const char *song_path = "./songs/简单爱.mp3";
const char *lrc_path = "../lrc/简单爱.lrc"; const char *lrc_path = "./lrcs/简单爱.lrc";
FILE *fp = open_lrc_file(lrc_path); // 打开歌词文件 FILE *lrc_fp = open_lrc_file(lrc_path); // 打开歌词文件
if (NULL == fp) long lrc_size = get_lrc_size(lrc_fp); // 获取并打印歌词文件大小
// printf("歌词大小为: %ld Byte\n", lrc_size);
char *lrc_mem_data = get_lrc_mem_data(lrc_fp); // 获取歌词数据
// 逐行解析歌词
char *lrc_lines[200] = {NULL}; // 定义歌词行指针数组
int i = 0; // 定义歌词行数
lrc_lines[i] = strtok(lrc_mem_data, "\r\n"); // 将歌词数据按行分割
while (lrc_lines[i] != NULL)
{ {
printf("歌词打开失败\n"); // 打印歌词
perror("fopen"); // time_delay(1); // 延时1秒
return -1; // 打歌词文件失败,退出程序 // printf("%d.\t%s\n", i, lrc_lines[i]); // 打歌词
i++; // 行数加1
lrc_lines[i] = strtok(NULL, "\r\n"); // 将剩余行歌词数据按行分割
} }
int rows = i; // 获取歌词总行数
handle_first_4_lines(lrc_lines); // 处理前 4 行歌词
// 保存歌词信息到链表
LRC *head = NULL; // 定义链表头指针
head = save_to_linklist(lrc_lines, rows); // 保存到链表s
// 开始播放歌曲
mplayer_play(song_path); // 启动mplayer播放器,播放歌曲 mplayer_play(song_path); // 启动mplayer播放器,播放歌曲
// 逐行显示歌词
show_lrcs(head); // 显示歌词
long lrc_size = get_lrc_size(fp); // 获取并打印歌词文件大小 // 释放内存,关闭文件
free(lrc_mem_data); // 释放歌词数据内存
char *lrc_mem_data = (char *)malloc(lrc_size); // 为歌词数据分配内存 free(head); // 释放链表内存
if (NULL == lrc_mem_data) fclose(lrc_fp); // 关闭歌词文件
{
printf("歌词数据分配内存失败\n");
perror("malloc");
return -1; // 为歌词数据分配内存失败,退出程序
}
return 0; return 0;
} }
View File
Executable → Regular
+21 -15
View File
@@ -1,26 +1,32 @@
#include <unistd.h> // usleep()
#include <stdio.h> #include <stdio.h>
#include <time.h> #include <time.h>
#include "includes/time_delay.h"
void time_delay(int sec) void time_delay(int sec)
{ {
int s_time,e_time; int s_time, e_time; // 定义开始时间和结束时间,单位为秒
s_time=time(NULL); s_time = time(NULL);
while(1) while (1)
{ {
e_time=time(NULL); e_time = time(NULL);
if(e_time==s_time+sec) if (e_time == s_time + sec)
break; break;
} }
} }
int main(int argc, char *argv[])
// 延迟指定毫秒数
void delay_ms(int milliseconds)
{ {
while(1) usleep(milliseconds * 1000); // usleep()函数用于毫秒级延迟
{
printf("hello world\n");
time_delay(3);
}
return 0;
} }
// 测试用例
// int main(int argc, char *argv[])
// {
// while(1)
// {
// printf("hello world\n");
// time_delay(3);
// }
// return 0;
// }