【开源】嵌入式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的分享

相关推荐
风静如云1 小时前
OpenBMC:BmcWeb定义service
linux
Ainnle1 小时前
企业级RAG开源项目分享:Quivr、MaxKB、Dify、FastGPT、RagFlow
人工智能·开源
sszdzq1 小时前
Docker
运维·docker·容器
book01211 小时前
MySql数据库运维学习笔记
运维·数据库·mysql
leoufung1 小时前
VIM FZF 安裝和使用
linux·编辑器·vim
bugtraq20212 小时前
XiaoMi Mi5(gemini) 刷入Ubuntu Touch 16.04——安卓手机刷入Linux
linux·运维·ubuntu
xmweisi2 小时前
【华为】报文统计的技术NetStream
运维·服务器·网络·华为认证
VVVVWeiYee2 小时前
BGP配置华为——路径优选验证
运维·网络·华为·信息与通信
陆鳐LuLu3 小时前
日志管理利器:基于 ELK 的日志收集、存储与可视化实战
运维·elk·jenkins
CodeWithMe3 小时前
[ Vim ] 常用命令 and 配置
linux·编辑器·vim