1.简介
此文章并不是教程,只能当作笔者的学习分享,只会做一些简单的介绍,其他的各位结合着代码和运行现象自己分析吧,相信通过函数名和注释,基本上是不难看懂代码的,其中涉及到的一些技术栈,也请各位学习到的时候多查阅资料。
本篇的内容为嵌入式Linux应用层的一个综合性比较强的项目,结尾会将源码放在网盘中开源出来,笔者能力有限,只是简单的把功能实现了,代码开源供大家一起交流学习,有什么好的建议,请各位一定不吝赐教!!!
1.1功能介绍
项目包括了四个app:
1.云平台的调试窗口,用于查看订阅主题所下发的数据,另一个为输入Json格式的数据来控制STM32单片机上的外设。
2.智能家居的界面,有4个图片按钮用于控制STM32板子上的LED灯、门(舵机)、蜂鸣器,量计分别为温度、湿度和亮度的值,同样是STM32获取发布到云平台的。
3.通过一个摄像头模块做的一个相机功能,可以拍照、录像,以及查看拍摄的照片,和播放录制视频的回放。
4.简易的音乐播放器:能够切换歌曲,以及暂停播放音乐。
1.2技术栈介绍
虽然项目简单,但是所涉及到的技术栈还是比较杂,我简单在此列出:
1.LVGL库用于绘制UI。
2.MQTT协议,连接阿里云平台与STM32通讯。
3.alsa库用于音频处理。
4.LED、BEEP
5.V4L2 摄像头应用编程
1.3演示视频
【开源】Linux应用综合项目|云平台调试工具+智能家居+相机+音乐播放器_哔哩哔哩_bilibili
1.4硬件介绍
硬件使用的是正点原子的阿尔法开发板,芯片是IMX6U,类似开发板应该都可以运行。
2.软件设计
2.1.asla音频应用编程
此文件初始化了声卡和混音器,创建了一个线程来播放我们所选的音乐,在切换和暂停歌曲时用到了互斥量来保护所要访问的资源,以防切换时出现错误。总共包括了4首歌曲,可以通过左右两个按键来切换上一首和下一首歌曲,可以调节音量和暂停开始播放。
cpp
#include "ds_music.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <alsa/asoundlib.h>
/************************************
宏定义
************************************/
#define PCM_PLAYBACK_DEV "hw:0,0"
#define MIXER_DEV "hw:0"
/************************************
WAV音频文件解析相关数据结构申明
************************************/
typedef struct WAV_RIFF
{
char ChunkID[4]; /* "RIFF" */
u_int32_t ChunkSize; /* 从下一个地址开始到文件末尾的总字节数 */
char Format[4]; /* "WAVE" */
} __attribute__((packed)) RIFF_t;
typedef struct WAV_FMT
{
char Subchunk1ID[4]; /* "fmt " */
u_int32_t Subchunk1Size; /* 16 for PCM */
u_int16_t AudioFormat; /* PCM = 1*/
u_int16_t NumChannels; /* Mono = 1, Stereo = 2, etc. */
u_int32_t SampleRate; /* 8000, 44100, etc. */
u_int32_t ByteRate; /* = SampleRate * NumChannels * BitsPerSample/8 */
u_int16_t BlockAlign; /* = NumChannels * BitsPerSample/8 */
u_int16_t BitsPerSample; /* 8bits, 16bits, etc. */
} __attribute__((packed)) FMT_t;
static FMT_t wav_fmt;
typedef struct WAV_DATA
{
char Subchunk2ID[4]; /* "data" */
u_int32_t Subchunk2Size; /* data size */
} __attribute__((packed)) DATA_t;
/************************************
static静态全局变量定义
************************************/
static snd_pcm_t *pcm = NULL; // pcm句柄
static snd_mixer_t *mixer = NULL; // 混音器句柄
static snd_mixer_elem_t *playback_vol_elem = NULL; // 播放<音量控制>元素
static unsigned int buf_bytes; // 应用程序缓冲区的大小(字节为单位)
static void *buf = NULL; // 指向应用程序缓冲区的指针
static int fd = -1; // 指向WAV音频文件的文件描述符
static snd_pcm_uframes_t period_size = 1024; // 周期大小(单位: 帧)
static unsigned int periods = 16; // 周期数(设备驱动层buffer的大小)
static struct termios old_cfg; // 用于保存终端当前的配置参数
static pthread_t g_play_music_thread = NULL;
static pthread_t g_mixer_music_thread = NULL;
bool is_playing = false;
int music_select = 1;
char *music_name, *music_singer;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t volume_mutex = PTHREAD_MUTEX_INITIALIZER;
static int snd_pcm_init(void)
{
snd_pcm_hw_params_t *hwparams = NULL;
int ret;
/* 打开PCM设备 */
ret = snd_pcm_open(&pcm, PCM_PLAYBACK_DEV, SND_PCM_STREAM_PLAYBACK, 0);
if (0 > ret)
{
fprintf(stderr, "snd_pcm_open error: %s: %s\n",
PCM_PLAYBACK_DEV, snd_strerror(ret));
return -1;
}
/* 实例化hwparams对象 */
snd_pcm_hw_params_malloc(&hwparams);
/* 获取PCM设备当前硬件配置,对hwparams进行初始化 */
ret = snd_pcm_hw_params_any(pcm, hwparams);
if (0 > ret)
{
fprintf(stderr, "snd_pcm_hw_params_any error: %s\n", snd_strerror(ret));
goto err2;
}
/**************
设置参数
***************/
/* 设置访问类型: 交错模式 */
ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
if (0 > ret)
{
fprintf(stderr, "snd_pcm_hw_params_set_access error: %s\n", snd_strerror(ret));
goto err2;
}
/* 设置数据格式: 有符号16位、小端模式 */
ret = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S16_LE);
if (0 > ret)
{
fprintf(stderr, "snd_pcm_hw_params_set_format error: %s\n", snd_strerror(ret));
goto err2;
}
/* 设置采样率 */
ret = snd_pcm_hw_params_set_rate(pcm, hwparams, wav_fmt.SampleRate, 0);
if (0 > ret)
{
fprintf(stderr, "snd_pcm_hw_params_set_rate error: %s\n", snd_strerror(ret));
goto err2;
}
/* 设置声道数: 双声道 */
ret = snd_pcm_hw_params_set_channels(pcm, hwparams, wav_fmt.NumChannels);
if (0 > ret)
{
fprintf(stderr, "snd_pcm_hw_params_set_channels error: %s\n", snd_strerror(ret));
goto err2;
}
/* 设置周期大小: period_size */
ret = snd_pcm_hw_params_set_period_size(pcm, hwparams, period_size, 0);
if (0 > ret)
{
fprintf(stderr, "snd_pcm_hw_params_set_period_size error: %s\n", snd_strerror(ret));
goto err2;
}
/* 设置周期数(驱动层buffer的大小): periods */
ret = snd_pcm_hw_params_set_periods(pcm, hwparams, periods, 0);
if (0 > ret)
{
fprintf(stderr, "snd_pcm_hw_params_set_periods error: %s\n", snd_strerror(ret));
goto err2;
}
/* 使配置生效 */
ret = snd_pcm_hw_params(pcm, hwparams);
snd_pcm_hw_params_free(hwparams); // 释放hwparams对象占用的内存
if (0 > ret)
{
fprintf(stderr, "snd_pcm_hw_params error: %s\n", snd_strerror(ret));
goto err1;
}
buf_bytes = period_size * wav_fmt.BlockAlign; // 变量赋值,一个周期的字节大小
return 0;
err2:
snd_pcm_hw_params_free(hwparams); // 释放内存
err1:
snd_pcm_close(pcm); // 关闭pcm设备
return -1;
}
static int snd_mixer_init(void)
{
snd_mixer_elem_t *elem = NULL;
const char *elem_name;
long minvol, maxvol;
int ret;
/* 打开混音器 */
ret = snd_mixer_open(&mixer, 0);
if (0 > ret)
{
fprintf(stderr, "snd_mixer_open error: %s\n", snd_strerror(ret));
return -1;
}
/* 关联一个声卡控制设备 */
ret = snd_mixer_attach(mixer, MIXER_DEV);
if (0 > ret)
{
fprintf(stderr, "snd_mixer_attach error: %s\n", snd_strerror(ret));
goto err;
}
/* 注册混音器 */
ret = snd_mixer_selem_register(mixer, NULL, NULL);
if (0 > ret)
{
fprintf(stderr, "snd_mixer_selem_register error: %s\n", snd_strerror(ret));
goto err;
}
/* 加载混音器 */
ret = snd_mixer_load(mixer);
if (0 > ret)
{
fprintf(stderr, "snd_mixer_load error: %s\n", snd_strerror(ret));
goto err;
}
/* 遍历混音器中的元素 */
elem = snd_mixer_first_elem(mixer); // 找到第一个元素
while (elem)
{
elem_name = snd_mixer_selem_get_name(elem); // 获取元素的名称
/* 针对开发板出厂系统:WM8960声卡设备 */
if (!strcmp("Speaker", elem_name) || // 耳机音量<对喇叭外音输出有效>
!strcmp("Headphone", elem_name) || // 喇叭音量<对耳机输出有效>
!strcmp("Playback", elem_name))
{ // 播放音量<总的音量控制,对喇叭和耳机输出都有效>
if (snd_mixer_selem_has_playback_volume(elem))
{ // 是否是音量控制元素
snd_mixer_selem_get_playback_volume_range(elem, &minvol, &maxvol); // 获取音量可设置范围
printf("minvol = %d, maxvol = %d\n", minvol, maxvol);
snd_mixer_selem_set_playback_volume_all(elem, (maxvol - minvol) * 0.9 + minvol); // 全部设置为90%
if (!strcmp("Playback", elem_name))
playback_vol_elem = elem;
}
}
elem = snd_mixer_elem_next(elem);
}
return 0;
err:
snd_mixer_close(mixer);
return -1;
}
static int open_wav_file(const char *file)
{
RIFF_t wav_riff;
DATA_t wav_data;
int ret;
fd = open(file, O_RDONLY);
if (0 > fd)
{
fprintf(stderr, "open error: %s: %s\n", file, strerror(errno));
return -1;
}
/* 读取RIFF chunk */
ret = read(fd, &wav_riff, sizeof(RIFF_t));
if (sizeof(RIFF_t) != ret)
{
if (0 > ret)
perror("read error");
else
fprintf(stderr, "check error: %s\n", file);
close(fd);
return -1;
}
if (strncmp("RIFF", wav_riff.ChunkID, 4) || // 校验
strncmp("WAVE", wav_riff.Format, 4))
{
fprintf(stderr, "check error: %s\n", file);
close(fd);
return -1;
}
/* 读取sub-chunk-fmt */
ret = read(fd, &wav_fmt, sizeof(FMT_t));
if (sizeof(FMT_t) != ret)
{
if (0 > ret)
perror("read error");
else
fprintf(stderr, "check error: %s\n", file);
close(fd);
return -1;
}
if (strncmp("fmt ", wav_fmt.Subchunk1ID, 4))
{ // 校验
fprintf(stderr, "check error: %s\n", file);
close(fd);
return -1;
}
/* 打印音频文件的信息 */
printf("<<<<音频文件格式信息>>>>\n\n");
printf(" file name: %s\n", file);
printf(" Subchunk1Size: %u\n", wav_fmt.Subchunk1Size);
printf(" AudioFormat: %u\n", wav_fmt.AudioFormat);
printf(" NumChannels: %u\n", wav_fmt.NumChannels);
printf(" SampleRate: %u\n", wav_fmt.SampleRate);
printf(" ByteRate: %u\n", wav_fmt.ByteRate);
printf(" BlockAlign: %u\n", wav_fmt.BlockAlign);
printf(" BitsPerSample: %u\n\n", wav_fmt.BitsPerSample);
/* sub-chunk-data */
if (0 > lseek(fd, sizeof(RIFF_t) + 8 + wav_fmt.Subchunk1Size,
SEEK_SET))
{
perror("lseek error");
close(fd);
return -1;
}
while (sizeof(DATA_t) == read(fd, &wav_data, sizeof(DATA_t)))
{
/* 找到sub-chunk-data */
if (!strncmp("data", wav_data.Subchunk2ID, 4)) // 校验
return 0;
if (0 > lseek(fd, wav_data.Subchunk2Size, SEEK_CUR))
{
perror("lseek error");
close(fd);
return -1;
}
}
fprintf(stderr, "check error: %s\n", file);
return -1;
}
long vol = 229;
void *play_music_thread(void *args)
{
int ret;
/* 播放 */
for (;;)
{
pthread_mutex_lock(&mutex);
while (!is_playing)
{
// 在暂停时关闭PCM设备
if (pcm != NULL)
{
snd_pcm_close(pcm);
pcm = NULL;
printf("PCM设备已暂停并关闭\n");
}
pthread_cond_wait(&cond, &mutex);
}
// 在恢复播放时重新初始化PCM设备
if (pcm != NULL)
{
snd_pcm_close(pcm);
pcm = NULL;
}
if (fd != -1)
{
close(fd);
fd = -1;
}
switch (music_select)
{
case 0:
if (open_wav_file("/home/root/wav_music/keximeiruguo.wav"))
goto err2;
break;
case 1:
if (open_wav_file("/home/root/wav_music/seeyoulater.wav"))
goto err2;
break;
case 2:
if (open_wav_file("/home/root/wav_music/xihuanni.wav"))
goto err2;
break;
case 3:
if (open_wav_file("/home/root/wav_music/xiyangwuxianhao.wav"))
goto err2;
break;
default:
printf("select music false\n");
goto err2;
break;
}
/* 初始化PCM Playback设备 */
if (snd_pcm_init())
goto err1;
/* 初始化混音器 */
if (snd_mixer_init())
goto err2;
/* 申请读缓冲区 */
buf = malloc(buf_bytes);
if (NULL == buf)
{
perror("malloc error");
goto err2;
}
pthread_mutex_unlock(&mutex);
while (is_playing)
{
memset(buf, 0x00, buf_bytes); // buf清零
ret = read(fd, buf, buf_bytes); // 从音频文件中读取数据
if (0 >= ret) // 如果读取出错或文件读取完毕
break;
ret = snd_pcm_writei(pcm, buf, period_size);
if (0 > ret)
{
fprintf(stderr, "snd_pcm_writei error: %s\n", snd_strerror(ret));
break;
}
else if (ret < period_size)
{ // 实际写入的帧数小于指定的帧数
// 此时我们需要调整下音频文件的读位置
// 将读位置向后移动(往回移)(period_size-ret)*frame_bytes个字节
// frame_bytes表示一帧的字节大小
if (0 > lseek(fd, (ret - period_size) * wav_fmt.BlockAlign, SEEK_CUR))
{
perror("lseek error");
break;
}
}
// 设置音量
pthread_mutex_lock(&volume_mutex);
long volume;
pthread_mutex_unlock(&volume_mutex);
snd_mixer_selem_get_playback_volume(playback_vol_elem,
SND_MIXER_SCHN_FRONT_LEFT, &volume);
printf("volume = %d\n", volume);
volume = vol;
snd_mixer_selem_set_playback_volume_all(playback_vol_elem, volume);
}
free(buf);
buf = NULL;
}
err2:
if (buf)
free(buf); // 释放内存
err1:
if (pcm != NULL)
{
snd_pcm_close(pcm); // 关闭pcm设备
pcm = NULL;
printf("已关闭pcm设备\r\n");
}
if (fd != -1)
{
close(fd); // 关闭打开的音频文件
fd = -1;
printf("音频已被关闭\r\n");
}
pthread_exit(NULL);
}
void *mixer_music_thread(void *args)
{
}
void clean_music_file(void)
{
// free(buf); // 释放内存
snd_pcm_close(pcm); // 关闭pcm设备
printf("已关闭pcm设备\r\n");
close(fd); // 关闭打开的音频文件
printf("音频已被关闭\r\n");
}
void ds_music_init(void)
{
int32_t res;
res = pthread_create(&g_play_music_thread, NULL, play_music_thread, NULL);
if (res < 0)
{
printf("pthread_create play_music_thread failed: %d\n", res);
return -1;
}
printf("play_music_thread created successfully\n");
res = pthread_create(&g_mixer_music_thread, NULL, mixer_music_thread, NULL);
if (res < 0)
{
printf("pthread_create mixer_music_thread failed: %d\n", res);
return -1;
}
printf("mixer_music_thread created successfully\n");
return 0;
}
2.2.音乐播放器UI设计
以下为音乐播放器的UI设计,基本上在前面了解了LVGL控件的设计之后,这部分看起来应该就没有什么难度了。
cpp
#include "ui_app_music.h"
#define MUSIC_NUM 4
// LV_IMG_DECLARE(disc_img);
LV_IMG_DECLARE(img_next_64);
LV_IMG_DECLARE(img_play_64);
LV_IMG_DECLARE(img_prev_64);
LV_IMG_DECLARE(img_stop_64);
LV_IMG_DECLARE(img_disc_256);
LV_IMG_DECLARE(img_love_48);
LV_IMG_DECLARE(img_love_off_48);
LV_IMG_DECLARE(img_sound_48);
static lv_obj_t *disc_img;
static lv_obj_t *prev_btn;
static lv_obj_t *play_pause_btn;
static lv_obj_t *next_btn;
static lv_obj_t *love_btn;
static lv_obj_t *sound_btn;
lv_obj_t *bg_click_area;
lv_obj_t *sound_slider;
lv_obj_t *sound_slider_label;
bool slider_visible = false; // 用于记录滑块是否可见
extern bool is_playing;
bool is_love;
extern char *music_name, *music_singer;
extern pthread_mutex_t mutex;
extern pthread_cond_t cond;
extern int music_select;
static void play_pause_event_cb(lv_event_t *e);
static void prev_event_cb(lv_event_t *e);
static void next_event_cb(lv_event_t *e);
static void love_event_cb(lv_event_t *e);
static void sound_event_cb(lv_event_t *e);
static void sound_slider_event_cb(lv_event_t *e);
static void bg_click_event_cb(lv_event_t *e);
void show_music_info(const char *name, const char *singer, const lv_font_t *font)
{
lv_obj_t *music_name_label = lv_label_create(lv_scr_act());
lv_label_set_text(music_name_label, name);
static lv_style_t music_name_label_style;
lv_style_init(&music_name_label_style);
lv_style_set_text_font(&music_name_label_style, font); // 设置字体
lv_style_set_text_color(&music_name_label_style, lv_color_hex(0x515151)); // 设置字体颜色
lv_obj_add_style(music_name_label, &music_name_label_style, LV_PART_MAIN);
lv_obj_align(music_name_label, LV_ALIGN_TOP_LEFT, 460, 130);
lv_obj_t *music_singer_label = lv_label_create(lv_scr_act());
lv_label_set_text(music_singer_label, singer);
static lv_style_t music_singer_label_style;
lv_style_init(&music_singer_label_style);
lv_style_set_text_font(&music_singer_label_style, font); // 设置字体
lv_style_set_text_color(&music_singer_label_style, lv_color_hex(0xbfbfbf)); // 设置字体颜色
lv_obj_add_style(music_singer_label, &music_singer_label_style, LV_PART_MAIN);
lv_obj_align(music_singer_label, LV_ALIGN_TOP_LEFT, 460, 160);
}
void show_music_info_select(int select)
{
clear_area(460, 130, 200, 60, lv_color_hex(MY_UI_COLOR_WHITE));
switch (select)
{
case 0:
show_music_info("可惜没有如果", "林俊杰", &lv_font_SiYuanHeiTi_Light_28);
break;
case 1:
show_music_info("See you later", "marmixer", &lv_font_montserrat_24);
break;
case 2:
show_music_info("喜欢你", "邓紫棋", &lv_font_SiYuanHeiTi_Light_28);
break;
case 3:
show_music_info("夕阳无限好", "陈奕迅", &lv_font_SiYuanHeiTi_Light_28);
break;
}
}
void create_music_player_ui(void)
{
int btn_x = 340, btn_y = 380;
music_name = "See you later";
music_singer = "marmixer";
show_music_info(music_name, music_singer, &lv_font_montserrat_24);
disc_img = lv_imgbtn_create(lv_scr_act());
lv_obj_set_size(disc_img, 256, 256);
lv_obj_add_event_cb(disc_img, prev_event_cb, LV_EVENT_CLICKED, NULL);
lv_img_set_src(disc_img, &img_disc_256);
lv_obj_align(disc_img, LV_ALIGN_TOP_LEFT, 145, 80);
prev_btn = lv_imgbtn_create(lv_scr_act());
lv_obj_set_size(prev_btn, 64, 64);
lv_obj_add_event_cb(prev_btn, prev_event_cb, LV_EVENT_CLICKED, NULL);
lv_img_set_src(prev_btn, &img_prev_64);
lv_obj_align(prev_btn, LV_ALIGN_TOP_LEFT, btn_x, btn_y);
play_pause_btn = lv_imgbtn_create(lv_scr_act());
lv_obj_set_size(play_pause_btn, 64, 64);
lv_obj_add_event_cb(play_pause_btn, play_pause_event_cb, LV_EVENT_CLICKED, NULL);
lv_img_set_src(play_pause_btn, &img_play_64);
lv_obj_align(play_pause_btn, LV_ALIGN_TOP_LEFT, btn_x + 80, btn_y);
next_btn = lv_imgbtn_create(lv_scr_act());
lv_obj_set_size(next_btn, 64, 64);
lv_obj_add_event_cb(next_btn, next_event_cb, LV_EVENT_CLICKED, NULL);
lv_img_set_src(next_btn, &img_next_64);
lv_obj_align(next_btn, LV_ALIGN_TOP_LEFT, btn_x + 80 * 2, btn_y);
love_btn = lv_imgbtn_create(lv_scr_act());
lv_obj_set_size(love_btn, 48, 48);
lv_obj_add_event_cb(love_btn, love_event_cb, LV_EVENT_CLICKED, NULL);
lv_img_set_src(love_btn, &img_love_48);
lv_obj_align(love_btn, LV_ALIGN_TOP_LEFT, 460, 260);
sound_btn = lv_imgbtn_create(lv_scr_act());
lv_obj_set_size(sound_btn, 48, 48);
lv_obj_add_event_cb(sound_btn, sound_event_cb, LV_EVENT_CLICKED, NULL);
lv_img_set_src(sound_btn, &img_sound_48);
lv_obj_align(sound_btn, LV_ALIGN_TOP_LEFT, 680, 260);
sound_slider = lv_slider_create(lv_scr_act());
lv_bar_set_range(sound_slider, 180, 255);
lv_obj_set_size(sound_slider, 10, 120);
lv_obj_align(sound_slider, LV_ALIGN_TOP_LEFT, 698, 110);
lv_obj_add_event_cb(sound_slider, sound_slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_add_flag(sound_slider, LV_OBJ_FLAG_HIDDEN);
lv_slider_set_value(sound_slider, 229, LV_ANIM_OFF);
// 更新标签
char buf[8];
int percentage = (229 - 180) * 100 / (255 - 180);
lv_snprintf(buf, sizeof(buf), "%d%%", percentage);
sound_slider_label = lv_label_create(lv_scr_act());
lv_label_set_text(sound_slider_label, buf);
lv_obj_align_to(sound_slider_label, sound_slider, LV_ALIGN_OUT_TOP_MID, 25, 65);
lv_obj_add_flag(sound_slider_label, LV_OBJ_FLAG_HIDDEN);
bg_click_area = lv_obj_create(lv_scr_act());
lv_obj_set_size(bg_click_area, LV_HOR_RES, LV_VER_RES);
lv_obj_add_flag(bg_click_area, LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_HIDDEN);
lv_obj_add_event_cb(bg_click_area, bg_click_event_cb, LV_EVENT_CLICKED, NULL);
lv_obj_move_background(bg_click_area);
}
static void play_pause_event_cb(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED)
{
printf("Button clicked!\n");
pthread_mutex_lock(&mutex);
is_playing = !is_playing;
if (is_playing)
{
lv_img_set_src(play_pause_btn, &img_stop_64); // 切换为暂停图标
pthread_cond_signal(&cond);
}
else
{
lv_img_set_src(play_pause_btn, &img_play_64); // 切换为播放图标
}
pthread_mutex_unlock(&mutex);
}
}
static void prev_event_cb(lv_event_t *e)
{
// Handle previous song action
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED)
{
printf("prev_event_cb clicked!\n");
pthread_mutex_lock(&mutex);
if (!is_playing)
{
music_select--;
if (music_select == -1)
music_select = MUSIC_NUM - 1;
}
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
printf("music_select = %d\n", music_select);
show_music_info_select(music_select);
}
}
static void next_event_cb(lv_event_t *e)
{
// Handle next song action
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED)
{
printf("next_event_cb clicked!\n");
pthread_mutex_lock(&mutex);
if (!is_playing)
{
music_select++;
if (music_select == MUSIC_NUM)
music_select = 0;
}
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
printf("music_select = %d\n", music_select);
show_music_info_select(music_select);
}
}
static void love_event_cb(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED)
{
printf("love_event_cb clicked!\n");
is_love = !is_love;
if (is_love)
{
lv_img_set_src(love_btn, &img_love_48); // 切换为暂停图标
}
else
{
lv_img_set_src(love_btn, &img_love_off_48); // 切换为播放图标
}
}
}
static void sound_event_cb(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED)
{
printf("sound_event_cb clicked!\n");
slider_visible = !slider_visible;
if (slider_visible)
{
lv_obj_clear_flag(sound_slider, LV_OBJ_FLAG_HIDDEN); // 显示滑块
lv_obj_clear_flag(sound_slider_label, LV_OBJ_FLAG_HIDDEN); // 显示标签
lv_obj_clear_flag(bg_click_area, LV_OBJ_FLAG_HIDDEN); // 显示背景点击区域
}
else
{
lv_obj_add_flag(sound_slider, LV_OBJ_FLAG_HIDDEN); // 隐藏滑块
lv_obj_add_flag(sound_slider_label, LV_OBJ_FLAG_HIDDEN); // 隐藏标签
lv_obj_add_flag(bg_click_area, LV_OBJ_FLAG_HIDDEN); // 隐藏背景点击区域
}
}
}
static void bg_click_event_cb(lv_event_t *e)
{
if (slider_visible)
{
lv_obj_add_flag(sound_slider, LV_OBJ_FLAG_HIDDEN); // 隐藏滑块
lv_obj_add_flag(sound_slider_label, LV_OBJ_FLAG_HIDDEN); // 隐藏标签
lv_obj_add_flag(bg_click_area, LV_OBJ_FLAG_HIDDEN); // 隐藏背景点击区域
slider_visible = false;
}
}
extern long vol;
extern pthread_mutex_t volume_mutex;
static void sound_slider_event_cb(lv_event_t *e)
{
lv_obj_t *slider = lv_event_get_target(e);
char buf[8];
long volume = (int)lv_slider_get_value(slider);
// 将滑条值转换为百分比
int percentage = (volume - 180) * 100 / (255 - 180);
pthread_mutex_lock(&volume_mutex);
vol = volume;
pthread_mutex_unlock(&volume_mutex);
printf("vol = %d\n", vol);
lv_snprintf(buf, sizeof(buf), "%d%%", percentage);
lv_label_set_text(sound_slider_label, buf);
lv_obj_align_to(sound_slider_label, slider, LV_ALIGN_OUT_TOP_MID, 25, 65);
}
void ui_app_music(void)
{
clear_area(0, 0, 800, 480, lv_color_hex(MY_UI_COLOR_DEEP_WHITE));
ui_left_app_bar(20, 70);
lv_obj_t *img = lv_img_create(lv_scr_act());
lv_img_set_src(img, &img_music_on);
lv_obj_align(img, LV_ALIGN_TOP_LEFT, 32, 360);
lv_obj_t *mid_label = lv_label_create(lv_scr_act());
lv_label_set_text(mid_label, "Music");
static lv_style_t mid_label_style;
lv_style_init(&mid_label_style);
lv_style_set_text_font(&mid_label_style, &lv_font_montserrat_24); // 设置字体
lv_style_set_text_color(&mid_label_style, lv_color_hex(MY_UI_COLOR_BLACK)); // 设置字体颜色
lv_obj_add_style(mid_label, &mid_label_style, LV_PART_MAIN);
lv_obj_align(mid_label, LV_ALIGN_TOP_MID, 0, 20);
create_split_background(lv_scr_act(), 135, 70, 640, 280, lv_color_hex(0xfefaec), lv_color_hex(MY_UI_COLOR_WHITE));
create_music_player_ui();
}
3.结尾(附网盘链接)
链接:百度网盘 请输入提取码
提取码:2jia
--来自百度网盘超级会员V5的分享