【开源】嵌入式Linux(IMX6U)应用层综合项目(4)--音乐播放器APP

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首歌曲,可以通过左右两个按键来切换上一首和下一首歌曲,可以调节音量和暂停开始播放。

#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");elsefprintf(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");elsefprintf(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控件的设计之后,这部分看起来应该就没有什么难度了。

#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 actionlv_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 actionlv_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的分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/393355.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图论(强联通分量)

在图论中&#xff0c;特别是在讨论有向图&#xff08;Directed Graph&#xff09;时&#xff0c;我们常常需要了解图的结构特性&#xff0c;比如强联通分量&#xff08;Strongly Connected Components, SCC&#xff09;。了解强联通分量中的各种边对于理解图的整体结构以及某些…

Redisson可重入锁原理(基于黑马视频总结,保姆级)

上一篇文章我们基于redis的set nx ex 命令以及Lua脚本实现了基本的分布式锁&#xff0c;但是还存在一下几点问题。于是又引出了redisson。 为什么基于SETNX的分布式锁无法实现可重入 先在method1中获取锁&#xff0c;获取成功后又调用method2&#xff0c;而method2内部也会获取…

spring+SSM+Mybatis面试题(上)(30道)

目录 1. 何为Spring Bean容器?Spring Bean容器与Spring IOC 容器有什么不同吗?2. Spring IOC 如何理解?3. Spring DI 如何理解?4. Spring 中基于注解如何配置对象作用域?以及如何配置延迟加载机制?1.配置作用域需要注解Scope(“Singleton”)2.开启延迟加载&#xff1a;La…

脚本:自动生成精准的Oracle AWR报告

很多朋友把AWR报告发过来让我帮忙分析Oracle数据库的性能&#xff0c;但很多报告都有一个共同的缺陷&#xff1a;就是这些报告覆盖的时间范围太广&#xff0c;导致性能问题的数据被严重稀释。 英文原文&#xff1a;Script: Generating Focused AWR Reports 为了解决这个问题&a…

完美解决pip命令版本冲突导致对应版本模块包无法安装的问题

解决步骤 使用pip更新/降低指定模块包命令格式降低pip自身至指定版本的命令再次换源安装指定模块包 在对 FasterNet 这篇论文源码复现过程中&#xff0c;我们首先需要安装相关依赖文件&#xff08; path/to/your/requirements.txt&#xff09; -extra-index-url https://down…

临床数据科学中如何用R来进行缺失值的处理(上)

在临床科研中&#xff0c;由于失访、无应答或记录不清等各种原因&#xff0c;经常会遇到数据缺失的问题。本文将深入探讨医学科研中数据缺失的成因、分类、影响以及应对方法&#xff0c;结合R语言的实际应用&#xff0c;为医学研究人员提供全面的解决方案。 一、认识缺失数据 …

【生成式人工智能-四-chatgpt的训练过程-pretrain预训练自督导式学习督导式学习】

大模型是怎么被训练出来的具有人类智慧的 阶段一训练-自我学习-具备知识训练资料self-supervised learning&#xff08;自督导式学习&#xff09; 阶段二-怎么让模型具备人的智慧supervised learning 督导式学习预训练pretrain为什么要用预训练的模型&#xff1f;Adapter逆向工…

红外遥控风扇——arduino

红外遥控风扇——arduino 本节课任务红外遥控红外遥控通信过程红外遥控套件红外遥控接线实现风扇的多种换挡方式用本节课所学的红外遥控&#xff0c;控制RGB彩灯变换颜色&#xff0c;至少配置4种 本节课任务 1、了解红外遥控技术在生活中的运用。 2、学会编程测试红外遥控器的…

WPF-实现多语言的静态(需重启)与动态切换(不用重启)

一、多语言切换&#xff08;需重启&#xff09; 1、配置文件添加Key <appSettings><add key"language" value"zh-CN"/></appSettings> 2、新增附加属性当前选择语言 public CultureInfo SelectLanguage{get > (CultureInfo)GetValu…

C#初级——List 容器

容器 在C#中&#xff0c;容器通常指的是用于存储和组织数据的集合类。 本文介绍的容器是动态数组&#xff1a;List<T> 内部使用数组来存储元素&#xff0c;当添加元素超出当前数组容量时&#xff0c;会自动调整大小&#xff08;扩容&#xff09;。 list容器 List<&g…

用数组表达双链表

大体思想跟单链表相同&#xff0c;只不过双链表每个节点有两个指向&#xff1a; 单链表只能指向一个节点(下一个节点) 而双链表可以指向两个节点(上下两个节点) 代码分析 1、定义 在这里没有定义head&#xff0c;直接让0号点是head&#xff0c;下标为1的点是最右边的 //e[i…

Spring Boot 中使用 JSON Schema 来校验复杂JSON数据

​ 博客主页: 南来_北往 系列专栏&#xff1a;Spring Boot实战 前言 在应用程序接口&#xff08;API&#xff09;的开发中&#xff0c;JSON作为一种数据交换格式被广泛利用。然而&#xff0c;对数据的结构和规则进行标准化是至关重要的&#xff0c;这正是JSON Schema发挥…

模拟一次XFS故障,分析原因并进行修复

模拟一次XFS故障 在平常处理问题时经常会遇到文件系统损坏的问题&#xff0c;有时候是日志里面出现了报错但文件系统还是可以读写&#xff0c;有时候是文件系统已经无法读写了 分析下不同现象的原因和一些可能出现的情况。 通过直接修改块存储损坏文件系统 1、制作一个xfs文…

Android图像显示SurfaceFlinger总结

1 介绍 1.1 框架中位置 ​​ 上图为Android的图形显示系统框架图。 首先上层应用通过ViewRoot的scheduleTraversals函数发起绘制任务&#xff0c;并通过HWUI调用OpenGL接口将绘制数据传递给GPU处理&#xff1b;SF会接收所有应用更新的绘制数据&#xff0c;并根据Z-Order、透明…

计算机网络(网络层)

网络层概述 网络层是干什么的&#xff1f; 网络层的主要任务是实现不同异构网络互连&#xff0c;进而实现数据包在各网络之间的传输相比于数据链路层的以太网通信&#xff0c;网络层则是将一个个数据链路层连接的以太网通过路由器连接起来。从而实现不同数据链路层的互联。 这…

​【香菇带你学Mysql】Mysql超长执行sql定位和优化【建议收藏】

本文为MySQL数据库管理员和开发人员提供了一套全面的超时SQL定位和优化解决方案。通过合理运用这些方法和技巧&#xff0c;可以显著提升MySQL数据库的性能和稳定性&#xff0c;减少超时SQL语句的发生&#xff0c;确保数据库的高效运行。 0. 引言 最近某个Mysql数据库频繁告警…

登录页滑块验证图

效果图 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head> <b…

【Kubernetes】k8s集群中pod的容器资源限制和三种探针

目录 一.关于pod容器的资源限制 1.资源限制的单位 1.1.CPU 资源单位 1.2.内存 资源单位 二.关于QOS服务质量&#xff08;pod的调度和驱逐有限制&#xff09; 1.QoS服务质量分类 2.驱逐顺序 三.关于pod容器的三种探针 1.探针的三种规则 2.Probe支持三种检查方法 3.探…

docker安装及使用

一、docker优点及作用 优点&#xff1a; 基础镜像MB级别创建简单隔离性强启动速度秒级移植与分享放便 作用&#xff1a;资源隔离 cpu、memory资源隔离与限制访问设备隔离与限制网络隔离与限制用户、用户组隔离限制 二、docker安装 2.1.配置yum源 yum install -y yum-uti…

Mysql开启SSL

等二测出未开启SSL,如下 have_openssl、have_ssl都是DISABLED也不知道当时为啥没开&#xff0c;看最近的都是开启的,整改必去得开了&#xff0c;开启步骤 1.生成秘钥 进入mysql的bin目录下&#xff0c;运行 ./mysql_ssl_rsa_setup运行后会生成证书 默认证书会在mysql的data…