47047| 13
|
手搓了一个「千千静听」 |
这个项目用到了LVGL和SquareLine Studio来设计音乐播放器的界面。 先看看效果: 装机展示: 任何带有屏幕和音频输出的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中发现的功能。 以下是我的功能愿望清单:
步骤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[0] != '.') && (strcmp(MP3_EXT, &filename[len - 4]) == 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[idx++] != 0xFF) || (coverImgFile[idx] != 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[raw_data_idx++] = *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:设计外壳(可选) 为开发设备设计一个漂亮的外壳,让它更像一个音乐播放器。 你可以在Thingiverse上找到WT32-SC01 PLUS桌面外壳: https://www.thingiverse.com/thing:6030590 步骤17:搞定! 是时候享受音乐了! 更多内容可以关注B站:陳亮手痕定律 链接:https://space.bilibili.com/1167567228 步骤18:待更新迭代 以下是我们可以进一步实现的功能愿望清单:
附件下载:千千静听 原文作者:陳亮 原文链接:https://www.instructables.com/Design-Music-Player-UI-With-LVGL/ 译文首发于公众号:DF创客社区 转载请注明来源信息 |
© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed