1.实现基本的音乐播放器功能 暂停,播放,音量调节 ,音乐切换
2.实现播放列表
3.实现进度条控制音乐
基于lvgl9.0库进行设计播放器ui,播放列表,进度条,按钮,利用多线程进行调用mplayer播放器进行音乐暂停,播放,音量调节 ,音乐切换。
主界面设置:
// ==========主界面 ================
void my_main(void)
{
//lv_obj_add_state(currentButton, LV_STATE_CHECKED);//向对象添加一个或多个状态。
lv_obj_t * label;
lv_obj_t * btn1 = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn1, event_handler_up, LV_EVENT_CLICKED,"last");
lv_obj_align(btn1, LV_ALIGN_BOTTOM_MID,-300, 0);
label = lv_label_create(btn1);
lv_label_set_text(label, "last");
lv_obj_center(label);
lv_obj_t *wplast = lv_img_create(label);
lv_img_set_src(wplast,"S:/demo/lvgl_img/last.jpg");
lv_obj_t * btn2 = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn2, event_handler_dn, LV_EVENT_CLICKED,"next");
lv_obj_align(btn2, LV_ALIGN_BOTTOM_MID,300, 0);
label = lv_label_create(btn2);
lv_label_set_text(label, "next");
lv_obj_center(label);
lv_obj_t *wpnext = lv_img_create(label);
lv_img_set_src(wpnext,"S:/demo/lvgl_img/next.jpg");
lv_obj_t * btn_start = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn_start, event_handler_play, LV_EVENT_CLICKED,"play");
lv_obj_align(btn_start, LV_ALIGN_BOTTOM_MID, 0, 0);
label = lv_label_create(btn_start);
lv_label_set_text(label, "play");
lv_obj_center(label);
lv_obj_t *wpplay = lv_img_create(label);
lv_img_set_src(wpplay,"S:/demo/lvgl_img/play.jpg");
lv_obj_t * btn_stop = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn_stop, event_handler_play, LV_EVENT_CLICKED,"stop");
lv_obj_align(btn_stop, LV_ALIGN_BOTTOM_MID, 0, -60);
label = lv_label_create(btn_stop);
lv_label_set_text(label, "stop");
lv_obj_center(label);
lv_obj_t *wpstop = lv_img_create(label);
lv_img_set_src(wpstop,"S:/demo/lvgl_img/stop.jpg");
lv_obj_t * btn_fast = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn_fast, event_handler_play, LV_EVENT_CLICKED,"fast");
lv_obj_align(btn_fast, LV_ALIGN_BOTTOM_MID,200, 0);
label = lv_label_create(btn_fast);
lv_label_set_text(label, "fast");
lv_obj_center(label);
lv_obj_t *wpfast = lv_img_create(label);
lv_img_set_src(wpfast,"S:/demo/lvgl_img/fast.jpg");
lv_obj_t * btn_slow = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn_slow, event_handler_play, LV_EVENT_CLICKED,"slow");
lv_obj_align(btn_slow, LV_ALIGN_BOTTOM_MID,-200, 0);
label = lv_label_create(btn_slow);
lv_label_set_text(label, "slow");
lv_obj_center(label);
lv_obj_t *wpslow = lv_img_create(label);
lv_img_set_src(wpslow,"S:/demo/lvgl_img/slow.jpg");
lv_obj_t * btn_control = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn_control, event_handler_play, LV_EVENT_CLICKED,"control+");
lv_obj_align(btn_control, LV_ALIGN_BOTTOM_LEFT,0, -250);
label = lv_label_create(btn_control);
lv_label_set_text(label, "control+");
lv_obj_center(label);
lv_obj_t *wpadd = lv_img_create(label);
lv_img_set_src(wpadd,"S:/demo/lvgl_img/add.jpg");
lv_obj_t * btn_control1 = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn_control1, event_handler_play, LV_EVENT_CLICKED,"control-");
lv_obj_align(btn_control1, LV_ALIGN_BOTTOM_LEFT,0, -180);
label = lv_label_create(btn_control1);
lv_label_set_text(label, "control-");
lv_obj_center(label);
lv_obj_t *wpredu = lv_img_create(label);
lv_img_set_src(wpredu,"S:/demo/lvgl_img/reduce.jpg");
/*创建进度滑动条*/
slider = lv_slider_create(lv_scr_act());
//设置宽度
lv_obj_set_content_width(slider,600);
lv_obj_align(slider, LV_ALIGN_BOTTOM_MID,0, -130);; //设置当前空间的原点坐标
//滑动条的值被改变时发送事件
lv_obj_add_event_cb(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
/*进度条在滑块下面创建一个标签 */
slider_label = lv_label_create(lv_scr_act());
lv_label_set_text(slider_label, "0:00");
//设置位置
lv_obj_align_to(slider_label, slider, LV_ALIGN_LEFT_MID, -50, 0);
slider_label1 = lv_label_create(lv_scr_act());
lv_label_set_text(slider_label1, "0:00");
//设置位置
lv_obj_align_to(slider_label1, slider, LV_ALIGN_RIGHT_MID, 40, 0);
//进度条
static lv_style_t style_indic;
//初始化样式
lv_style_init(&style_indic);
lv_style_set_bg_opa(&style_indic, LV_OPA_COVER);
lv_style_set_bg_color(&style_indic, lv_palette_main(LV_PALETTE_RED));
lv_style_set_bg_grad_color(&style_indic, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_bg_grad_dir(&style_indic, LV_GRAD_DIR_VER);
//创建进度条
lv_obj_t * bar1 = lv_bar_create(lv_scr_act());
lv_obj_add_style(bar1, &style_indic, LV_PART_INDICATOR);
lv_obj_set_size(bar1, 20, 200);
lv_obj_align(bar1, LV_ALIGN_RIGHT_MID,-220, -30);
lv_bar_set_range(bar1, -20, 40);
//动画
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_exec_cb(&a, set_temp);//设置一个函数使“var”动画化
lv_anim_set_time(&a, 300);//设置动画的持续时间
lv_anim_set_playback_time(&a, 300);//使动画回放到前进方向准备好时
lv_anim_set_var(&a, bar1);//设置一个变量为animate
lv_anim_set_values(&a, -20, 40);//设置动画的开始和结束值
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);//让动画重复自己。参数给0停止动画。
lv_anim_start(&a);
//创建进度条
bar = lv_bar_create(lv_scr_act());
lv_obj_add_style(bar, &style_indic, LV_PART_INDICATOR);
lv_obj_set_size(bar, 20, 200);
lv_obj_align(bar, LV_ALIGN_LEFT_MID, 190, -50);
lv_bar_set_range(bar, 0, 100);
volume = 100;
lv_bar_set_value(bar, volume, LV_ANIM_OFF);
/*进度条在上面创建一个标签 */
volume_label = lv_label_create(lv_scr_act());
lv_label_set_text(volume_label, "100");
//设置位置
lv_obj_align_to(volume_label, bar, LV_ALIGN_TOP_MID, 0, -20);
lv_obj_t * label_imgs = lv_img_create(lv_scr_act());
lv_obj_set_size(label_imgs,30,30);
lv_obj_align_to(label_imgs, bar, LV_ALIGN_BOTTOM_MID, 0, 33);
lv_img_set_src(label_imgs,"S:/demo/yinxiang.jpg");
}
通过按钮触发事件然后新建线程启动播放器mplayer命令:mplayer -slave -quiet -input file=/pipe bsj.mp3 播放下一首需要关闭原有的线程和mplayer进程 killall -9 mplayer
部分代码演示:
//播放暂停快进等按钮操作
static void event_handler_play(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
//获取传递的参数
char *bt = lv_event_get_user_data(e);
char bufshow[1024] = {0};
if(code == LV_EVENT_CLICKED) {
if(strcmp(bt,"play") == 0)
{
system("killall -9 mplayer") ; ;//关闭原有的进程
pthread_create(&tid,NULL,music_task,NULL);
sleep(1);
//取消前一个调用的更新线程
pthread_cancel(getstr_tid);
pthread_cancel(upbar_tid);
pthread_create(&getstr_tid,NULL,music_task_getstr,NULL);
pthread_create(&upbar_tid,NULL,update_progree_bar,NULL);
}
if(strcmp(bt,"stop") == 0)
{
if(stop_state == 0)
{
pthread_cancel(getstr_tid);
pthread_cancel(upbar_tid);
char cmd[1024]={"pause\n"};
write(fd_pipe,cmd,strlen(cmd));
stop_state = 1;
}else
{
pthread_create(&getstr_tid,NULL,music_task_getstr,NULL);
pthread_create(&upbar_tid,NULL,update_progree_bar,NULL);
char cmd[1024]={"pause\n"};
write(fd_pipe,cmd,strlen(cmd));
stop_state = 0;
}
}
if(strcmp(bt,"fast") == 0)
{
char cmd[1024]={"seek +5\n"};
write(fd_pipe,cmd,strlen(cmd));
}
if(strcmp(bt,"slow") == 0)
{
char cmd[1024]={"seek -5\n"};
write(fd_pipe,cmd,strlen(cmd));
}
if(strcmp(bt,"control+")== 0 )
{
volume ++ ;
if(volume >= 100) volume =100;
char cmd[1024]={0};
sprintf(cmd,"volume %d 1\n",volume);
write(fd_pipe,cmd,strlen(cmd));
char buf[12] = {0};
sprintf(buf,"%d",volume);
}
if(strcmp(bt,"control-")==0)
{
volume -- ;
if(volume <= 0) volume =0;
char cmd[1024]={0};
sprintf(cmd,"volume %d 1\n",volume);
write(fd_pipe,cmd,strlen(cmd));
lv_bar_set_value(bar, volume, LV_ANIM_OFF);
char buf[12] = {0};
sprintf(buf,"%d",volume);
lv_label_set_text(volume_label, buf);
}
}
}
开启一个线程不断读取popen打开mplayer输出的东西打印到进度条标签实现进度条时间变化 根据进度条时间调节mplayer的播放时间
部分代码演示:
void *music_task_getstr(void *arg)
{
//获取歌曲总时间
char cmd[1024]={"get_time_length\n"};
write(fd_pipe,cmd,strlen(cmd));
while(1)
{
char msg[1024] = {0};
while(1)
{
// 通过 fgets 函数可以从管道文件中直接读取到视频播放进程所有输出的内容
// 然后就可以为所欲为了
char* p = fgets(msg , 1024 , fp );
if (p == NULL )
{
break ;
}
if(strstr(msg,"ANS_LENGTH"))
{
sscanf(msg,"ANS_LENGTH=%f",&music_total_time);
printf("all time :%.2f\n",music_total_time);
char buf[124] = {0};
int m = (int)music_total_time / 60;
int s = (int)music_total_time % 60;
if(s <= 9)
{
sprintf(buf,"0%d:0%d",m,s);
}else
{
sprintf(buf,"0%d:%d",m,s);
}
printf("slider_label1 buf:%s\n",buf);
//上锁
pthread_mutex_lock(&mux);
lv_label_set_text(slider_label1, buf);//显示歌曲总时间秒数 150s 150/60 150%60 2分30秒
//解锁
pthread_mutex_unlock(&mux);
}
if(strstr(msg,"ANS_TIME_POSITION"))
{
sscanf(msg,"ANS_TIME_POSITION=%f",&music_current_time);
printf("all time :%.2f\n",music_current_time);
}
printf("读取到的信息:%s" , msg );
}
}
}
void *update_progree_bar(void *arg)
{
while (1)
{
char cmd[1024]={"get_time_pos\n"};
write(fd_pipe,cmd,strlen(cmd));
char buf[124] = {0};
int m = (int)music_current_time / 60;
int s = (int)music_current_time % 60;
if(s <= 9)
{
sprintf(buf,"0%d:0%d",m,s);
}else
{
sprintf(buf,"0%d:%d",m,s);
}
pthread_mutex_lock(&mux);
lv_label_set_text(slider_label, buf);//显示进度秒数 150s 150/60 150%60 2分30秒
pthread_mutex_unlock(&mux);
//更新滑块位置 0~100 播放时间/总时间
int location = (music_current_time / music_total_time) *100 ;
pthread_mutex_lock(&mux);
lv_slider_set_value(slider,location, LV_ANIM_OFF);
pthread_mutex_unlock(&mux);
sleep(1);
}
}
对比歌曲文件显示歌曲对应的图片信息
部分代码演示:
//显示对应歌曲的图片
if(strstr(all_pic[music_index],"bsj.mp3"))
{
lv_img_set_src(wp,"S:/demo/lvgl_img/bsj.jpg");
}else if(strstr(all_pic[music_index],"yudao.mp3"))
{
lv_img_set_src(wp,"S:/demo/lvgl_img/yudao.jpg");
}else if(strstr(all_pic[music_index],"yanyuan.mp3"))
{
lv_img_set_src(wp,"S:/demo/lvgl_img/yanyuan.jpg");
}
出现这个错误:
[Error] (25.685, +8120) _lv_inv_area: detected modifying dirty areas in render (in lv_refr.c line #
考虑到线程安全问题,自己创建的线程调用lv类函数时与lvgl本身刷图像时调用产生了冲突。解决方法直接百度lvgl线程安全
如果需要使用实际的任务或线程,则需要一个互斥锁,该互斥锁应在调用 lv_task_handler 之前被调用,并在其之后释放。同样,必须在与每个LVGL(lv _...)相关的函数调用和代码周围的其他任务和线程中使用相同的互斥锁。这样,就可以在真正的多任务环境中使用LVGL。只需使用互斥锁(mutex)即可避免同时调用 LVGL 函数。
第一个是切换音乐 这里卡了有点久 关闭线程一直没用 然后才发现得杀死播放音乐的进程才能达到创建新的线程来播放下一首音乐
第二个是 进度条功能,我的思路是设计三个线程,一个线程用来获取poen打印mplayer的信息 一个线程用来更新时间标签 这里也有问题,在播放下一首的时候需要把之前线程取消再创建
在做项目过程中进一步加深系统编程的知识内容的理解,认识到多线程在程序中使用的重要性,代码编写更加规范,提高自己对做项目的整体框架思路 使用lvgl来编写图形界面更加方便 进一步加深自己对框架编程的理解和运用