手搓了一个「千千静听」
这个项目用到了LVGL和SquareLine Studio来设计音乐播放器的界面。
先看看效果:
https://dfrobot.loll.cc/video/Install_Winamp_on_your_desktop_in_the_right_way.mp4
装机展示:
https://dfrobot.loll.cc/video/LVGL_music_player_full.mp4
任何带有屏幕和音频输出的ESP32开发设备都可以。
以下是作者在本教程中开发用到的东西:
https://item.taobao.com/item.htm?id=694922892355
https://www.aliexpress.com/item/1005004267336768.html
https://www.makerfabs.com/esp32-s3-parallel-tft-with-touch-4-inch.html
https://www.aliexpress.com/item/1005004952726089.html
步骤1:什么是LVGL?
LVGL是一个很好的图形库,可以轻松制作漂亮的GUI界面。而SquareLine Studio可以帮助我们减少大量的编码工作。
官方解释:
LVGL (Light and Versatile Graphics Library)是一个开源的嵌入式图形库,用于创建图形用户界面 (GUI)。它被设计为尽可能轻量、灵活和可扩展,适用于各种嵌入式系统。LVGL提供了丰富的图形元素和控件,可以帮助开发者快速创建漂亮和交互性强的用户界面。它具有跨平台的特性,支持多种显示控制器和输入设备。LVGL还提供了易于使用的API和丰富的文档,使开发人员能够高效地构建和定制他们的应用程序界面。
步骤2:项目源文件
所有图像源文件、SquareLine Studio项目和源代码都可以在Github上找到:https://github.com/moononournation/LVGL_Music_Player.git
也可以在文末打包下载。
步骤3:界面设计
当前的开发设备具有不同形状的显示屏,圆形、正方形、长方形都有,所以建议最好先确定使用哪种形状。
然后下一步是确定界面的外观。
步骤4:经典音乐播放器
如果你还没有任何界面设计的想法,那么建议可以模仿一个经典的音乐播放器。
Winamp是一个经典的Windows桌面音乐播放器,上个世纪就问世了,然后有一个名为TTPlayer的类似的界面变体,支持中文字符。这两个应用程序是我(和许多人)的童年记忆,所以我将使用这些界面作为设计模板。
Winamp设计得可以轻松更换"皮肤",皮肤图像资源以BMP格式打包在一个zip文件中。你可以在skins.webamp.org找到大量的Winamp皮肤收藏,很容易可以把你喜欢的Winamp皮肤作为设计的模板。
如何从Winamp皮肤开始设计界面,可以看这个视频:
https://www.bilibili.com/video/BV1ia4y137KM/
步骤5:功能愿望清单
音乐播放器具有各种功能,但我更感兴趣的是在Winamp或TTPlayer中发现的功能。
以下是我的功能愿望清单:
[*]支持从SD卡播放MP3
[*]支持从SD卡列出MP3文件
[*]支持显示Unicode字符
[*]支持基本播放操作(播放、暂停、停止、上一曲和下一曲)
[*]支持音量控制
[*]支持显示MP3的ID3信息
[*]支持显示MP3的封面图片
[*]支持显示MP3歌词
[*]支持播放时同步显示歌词
[*]支持显示音频频谱分析仪
步骤6:从SD卡播放MP3
首先,我们需要一个音频库,可以从SD卡读取MP3文件并将其播放出来。这次我使用的是ESP32-audioI2S。它支持ESP32系列,可以从各种来源读取音频文件并将输出播放到I2S模块。
你可以在Github上找到更多详细信息:https://github.com/schreibfaul1/ESP32-audioI2S.git
步骤7:基本播放操作
ESP32-audioI2S提供了所有基本的播放API,我们只需要为每个操作创建相应的按钮部件。不过,原始的Winamp按钮设计对于用手指触摸屏幕操作来说太小了,所以我稍微放大了按钮,并用透明背景扩大了触摸区域。
对于每个按钮,将按钮部件分配相应的函数。
以播放按钮为例:
lv_obj_add_event_cb(ui_ButtonPlay, playSong, LV_EVENT_CLICKED, NULL);
然后在playSong函数中,调用ESP32-audioI2S API:
void playSong(lv_event_t *e)
{
if (isPlaying)
{
audio.pauseResume();
}
else
{
play_selected_song();
}
}
步骤8:从SD卡列出MP3文件
在告诉ESP32-audioI2S播放MP3文件之前,我们需要先从SD卡找到并列出MP3文件。
以下是read_song_list()函数的代码片段,说明了如何使用换行符(\n)拼接歌曲列表字符串:
File root = SD_MMC.open("/");
File file = root.openNextFile();
while (file)
{
if (file.isDirectory())
{
Serial.printf("DIR: %s\n", file.name());
}
else
{
const char *filename = file.name();
int8_t len = strlen(filename);
const char *MP3_EXT = ".mp3";
if ((filename != '.') && (strcmp(MP3_EXT, &filename) == 0))
{
// Serial.printf("Song file: %s, size: %d\n", filename, file.size());
if (song_count > 0)
{
stringSongList += '\n';
}
stringSongList += filename;
song_count++;
}
}
file = root.openNextFile();
}
然后将拼接好的歌曲列表字符串分配给LVGL的滚动组件:
lv_roller_set_options(ui_RollerPlayList, stringSongList.c_str(), LV_ROLLER_MODE_INFINITE);
步骤9:显示Unicode字符
LVGL支持显示Unicode字符,但需要一个Unicode字体文件。我的歌曲列表主要是中文字符,所以我选择了3种字体来显示它:
https://github.com/ACh-K/Cubic-11.git
https://fonts.google.com/noto/specimen/Noto+Sans+HK/glyphs
https://fonts.google.com/noto/specimen/Noto+Serif+HK/glyphs
然后使用SquareLine Studio的字体工具创建C源文件。
步骤10:音量控制
将音量滑块部件分配给一个值更改事件函数:
lv_obj_add_event_cb(ui_ScaleVolume, volumeChanged, LV_EVENT_VALUE_CHANGED, NULL);
然后在事件函数中调用ESP32-audioI2S API:
void volumeChanged(lv_event_t *e)
{
int16_t volume = lv_slider_get_value(ui_ScaleVolume);
audio.setVolume(volume);
}
时间进度UI也是一个滑块部件。但是它离按钮和音量控制UI太近了,所以我禁用了触摸输入,避免误触。
步骤11:显示MP3的ID3信息
ESP32-audioI2S公开了一个audio_id3data()回调函数。该函数会在每个MP3文件中找到一个ID3标签时调用。
在回调函数中,简单地将所有数据连接到一个字符串中:
if (playingStr.length() > 0)
{
playingStr += " ";
}
playingStr += info;
然后将其分配给一个标签以供显示:
lv_label_set_text(ui_LabelPlaying, playingStr.c_str());
步骤12:显示MP3封面图片
ESP32-audioI2S公开了一个audio_id3image()回调函数。如果在MP3的ID3标签中找到封面图片,则调用此函数。图片可以是任何图片格式,目前只支持解码和显示非渐进式JPEG图像文件。
在回调函数中,复制二进制数据:
file.seek(pos);
file.read(coverImgFile, len);
查找JPEG头:
size_t idx = 11;
while ((idx < len) && ((coverImgFile != 0xFF) || (coverImgFile != 0xD8)))
;
--idx;
然后使用JPEGDEC解码:
jpegdec.openRAM(coverImgFile + idx, len - idx, jpegDrawCallback);
步骤13:显示MP3歌词
ESP32-audioI2S公开了一个audio_id3lyrics()回调函数。如果在MP3文件中找到了同步歌词、非同步歌词或文本数据标签,就会调用该函数。
在回调函数中,复制二进制数据:
file.seek(pos);
file.read((uint8_t *)lyricsText, len);
将二进制解码为UTF8文本:
audio.unicode2utf8(lyricsText, len);
如果文本中有时间标记,将时间索引存储到syncTimeLyricsSec[]和syncTimeLyricsLineIdx[]数组中。
然后将歌词文本设置为滚轮部件:
lv_roller_set_options(ui_RollerLyrics, lyricsText, LV_ROLLER_MODE_NORMAL);
步骤14:播放时同步显示歌词
如果找到了同步歌词标记,播放时滚动歌词部件:
for (int i = 0; i < syncTimeLyricsCount; ++i)
{
if (syncTimeLyricsSec == currentTime)
{
lv_roller_set_selected(ui_RollerLyrics, syncTimeLyricsLineIdx, LV_ANIM_ON);
break;
}
}
步骤15:显示音频频谱
ESP32-audioI2S还公开了一个audio_process_i2s()回调函数,用于处理音频输出。我们可以利用这个函数收集音频数据,以可视化音频频谱。
在回调函数中,收集音频数据:
raw_data = *sample;
如果数据已满,使用FFT类进行处理:
if (raw_data_idx >= WAVE_SIZE)
{
fft.exec((int16_t *)raw_data);
draw_fft_level_meter(canvasFFT_gfx);
lv_obj_invalidate(ui_CanvasFFT);
raw_data_idx = 0;
}
注意:
可视化是绘制到一个分开的画布canvasFFT_gfx上的。画布与LVGL的部件ui_CanvasFFT相关联。
步骤16:设计外壳(可选)
https://dfrobot.loll.cc/video/Install_Winamp_on_your_desktop_in_the_right_way-%5BusRQMTQ9-7A%5D.mp4
为开发设备设计一个漂亮的外壳,让它更像一个音乐播放器。
你可以在Thingiverse上找到WT32-SC01 PLUS桌面外壳:
https://www.thingiverse.com/thing:6030590
步骤17:搞定!
是时候享受音乐了!
更多内容可以关注B站:陳亮手痕定律
链接:https://space.bilibili.com/1167567228
步骤18:待更新迭代
以下是我们可以进一步实现的功能愿望清单:
[*]随机播放列表顺序
[*]LVGL滚轮无法处理大型列表,最好能够按文件夹列出歌曲
[*]MP3时间轴寻找
[*]支持更多的封面图片格式
[*]连接到互联网?目前暂时还没有任何想法
附件下载:千千静听
原文作者:陳亮
原文链接:https://www.instructables.com/Design-Music-Player-UI-With-LVGL/
译文首发于公众号:DF创客社区
转载请注明来源信息
技术真是太强了{:6_209:} 厉害!! 赞赞赞赞赞 看着真棒,技术太厉害了 这个不错呀
帅气啊,学习了 要是薄一点就更好了,最好加个支架 厉害厉害 赞!!!! 666 酷 厉害厉害厉害
好技术,厉害。做的很棒
页:
[1]