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

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

相关推荐
椰椰椰耶6 分钟前
【Linux】常用的命令
linux·运维·服务器
xiaojiesec1 小时前
第145天:内网安全-Linux权限维持&Rootkit后门&Strace监控&Alias别名&Cron定时任务
linux·运维·安全
安全在心中2 小时前
python-网页自动化(三)
运维·python·自动化
Slow2 小时前
自动化焊缝定义程序fe-safe
运维·python·自动化
爱吃龙利鱼2 小时前
web群集--nginx常见的几种负载均衡调度算法的配置过程和效果展示
运维·算法·nginx·云原生·负载均衡
阿洵Rain3 小时前
【Linux】环境变量
android·linux·javascript
冰 河4 小时前
《Nginx核心技术》第16章:实现Nginx的高可用负载均衡
运维·nginx·程序员·负载均衡·高可用
zealous_zzx4 小时前
深度解析Linux系统和Unix系统的基本概念及优缺点和原理
linux·unix
丢爸4 小时前
网络学习-eNSP配置NAT
linux·网络·学习
沐风ya5 小时前
NAT技术介绍+缺陷(内网穿透+工具),NAPT(介绍,替换过程,原理,NAT转换表)
linux·服务器·网络