AI脑洞板
完整教案
自学课件
第 38 课 · 共 38/51 课

音乐播放器 把媒体文件变成可点的歌单

课时
待定
器材
ESP32-S3 触摸屏板 + USB 线
前置知识
无
配套代码
见项目 demo 目录

你好,我是吴老师。这份是给你自己读的——不用等我讲,看不懂就整段复制给 AI,加一句"请一次只解释一个概念,讲完问我一个小问题"。

读完之后,你应该能:

  • 说出"歌单在板子里到底是什么"(一个数组);
  • 明白"现在放到第几首"是靠一个下标记住的;
  • 说清"点一首歌/切一首歌"在程序里是怎么做到先停再放的。

用时:约 60 分钟。 准备:烧录 demo01_music_player 的板子 + 一张拷了 .mp3 的 TF 卡 + 一根 USB 线。

一、从"按顺序放"到"能点的歌单"

第 16 课我们做了个掌上音乐盒,把 TF 卡里的一首歌"读→解码→喂 I2S→出声"放了出来。可是它只会按顺序一首首往下放,你想听中间那首,得等它唱到。

这次升级:TF 卡里放一大堆歌,板子把所有歌列成一份歌单,你想听哪首就点哪首。

先看现象:上电后屏幕自动列出歌单并开始唱第一首,你手指点任意一首,它立刻切过去。

二、我观察到的现象

✏️ 可以直接在表格里填写
现象勾选
上电后自动把所有歌列成歌单,开始唱第一首
点歌单某一行,立刻切到那一首
点 NEXT / PREV 能在上一首、下一首间切换
点 VOL+ / VOL- 声音变大变小
一首唱完,自动接着唱下一首
进度条在走,显示"第几秒/总长多少"

三、从"文件"到"歌单"的几站

环节谁在干哪课学过
扫 描scanDirectory() 递归翻 SD 文件夹,收所有 .mp3第 14 课
排歌单文件名存进数组 tracks[],sortTracks() 排序本课重点
放一首下标 currentTrack 指着"第几首",selectTrack() 先停再放第 16 课
解码+喂声ESP32-audioI2S 的 Audio 库边解码边喂 I2S第 16 课
出 声ES8311 声卡 + 扬声器第 11–13 课

跟第 16 课最大的不同:第 16 课是"点一首歌",这一课是"先扫出全部歌名做成数组,再反复从数组里取出一个下标来放"。

四、歌单,就是一排"有编号的抽屉"

概念像什么在代码里
歌单有编号的抽屉柜数组 tracks[]:tracks[0] 第 1 首、tracks[1] 第 2 首……
现在第几首一支手指下标 currentTrack:一个整数
切歌手指挪一下下标 +1 / 下标 −1

⚠️ 抽屉只是比喻,板子里没抽屉。tracks[] 存的是一串文件路径字符串,currentTrack 只是一个整数。记住"下标"这个词:它就是"第几个"的整数,用来在数组里定位。

一句话:歌单 = 数组(存歌名)+ 下标(记住现在第几首);切歌 = 让下标前移/后移。

歌单 = 一排有编号的抽屉柜:track 数组存歌名、currentTrack 下标记住现在第几首、切歌下标±1

五、两个代码名,够了

1. scanDirectory(SD_MMC, "/", 0)——把 TF 卡里的歌都扫出来

scanDirectory:递归翻 TF 卡文件夹。
  从根目录 "/" 开始,遇到子文件夹再往下一层(最多 4 层);
  遇到结尾是 ".mp3" 的,就把路径存进数组 tracks[]。
  最后 sortTracks() 按字母排队,就成了屏幕上的歌单。

屏幕上的那一列"第 1 首、第 2 首……",不是画出来的装饰,是把 tracks[] 一行行打印出来。 你改了 TF 卡里的歌,数组就变,屏幕歌单就跟着变。歌单在板子里,就是这一个数组。

2. selectTrack(index)——把播放头挪到第 index 首,然后放它

selectTrack(index):
  先 audio.stopSong()          // 停掉正在放的那首
  currentTrack = index          // 播放头挪到"第 index 首"
  audio.connecttoFS(SD_MMC, tracks[index])   // 把第 index 首接进音乐管家

你点歌单某一行、或按 NEXT/PREV,最后都落到 selectTrack(someIndex),只是那个下标程序帮你算:下一首 = (currentTrack + 1) % trackCount;上一首 = (currentTrack - 1 + trackCount) % trackCount。那个 %(取余)就是"到最后一首就绕回第一首"。

最该记住的:歌单是 tracks[] 数组;播放位置是 currentTrack 下标。点歌/切歌 = 先 stopSong(),再把 currentTrack 改成一个新下标,最后 connecttoFS() 放那一首。

scanDirectory 递归扫出歌名存数组、sortTracks 排队;selectTrack 先 stopSong 再 connecttoFS 先停再放

六、用手指点歌:这块板子真的能"点"

再留意这些细节,全都是"唱一首歌"之外的小机关:

✏️ 可以直接在表格里填写
现象勾选
点歌单某一行,顶部歌名和进度条立刻换成那首
点 NEXT 换下一首,点 PREV 换回上一首
点 VOL+ 屏幕"VOL n"变大,VOL- 变小
点 PAUSE 按键变 PLAY,声音停住;再点继续
一首唱完,不点按钮,它自己跳到下一首
正在放的那一行底色高亮

为什么切歌不跟上一首搅在一起? 因为 selectTrack() 先 audio.stopSong() 把旧歌停干净再换新的——像 CD 机换碟,先停再放。

一首唱完怎么自动接下一首? 靠 audio_eof_mp3 这个"播完回调":放完就把 trackFinished 置真,主循环一看到就自动下标+1。

七、改一个条件:随机 / 超过 64 首

思想实验,不用真改代码:

✏️ 可以直接在表格里填写
我改了什么我预测实际
把"下一首"改成"随机挑一首"把顺序的"下标+1"改成随机取下标;可能随机到同一首,要"直到不等于当前首"
TF 卡里放 100 首,超过 64 首MAX_TRACKS = 64 是上限,超出扫不进歌单,屏幕上只显示前 64 首

要"随机",只是把"下标+1"换个取下标的方式;要"多放几百首",只是把数组上限调大。 核心的"数组 + 下标"一点没变——这也是把歌单做成数组的好处。

八、一条完整的"点一首歌"链路

TF 卡里的一堆 MP3 → scanDirectory 递归扫描出文件名
→ 文件名存进数组 tracks[] 并 sortTracks 排队
→ 屏幕把 tracks 列成歌单
→ 手指点某一行 / 按 NEXT 或 PREV → 算出一个下标 index
→ selectTrack:先 stopSong 停掉当前
→ currentTrack = index, connecttoFS 放这一首
→ Audio 解码 + 小段小段喂 I2S → 扬声器出声
→ drawProgress 用播放时间/总长 画进度条
→ 一首放完 audio_eof_mp3 → 自动下标+1 放下一首

真正要记住的只有一句:歌单 = 存文件名的数组 tracks[];播放位置 = 下标 currentTrack;点歌/切歌 = 让下标指着"第几首",先停再放,唱完自动下标+1。

点一首歌的完整链路:扫描→排歌单→屏幕列→点行算下标→先停再放→解码喂I2S→画进度条,唱完自动接下一首

九、过关检查

  1. 板子怎么知道"一共有哪些歌"?"扫描"把什么变成了什么?
  2. 歌名存进数组后,靠什么记住"现在放到第几首"?
  3. "下一首"在程序里是"下标 +几"?为什么要"取模"?
  4. selectTrack() 接新歌之前为什么要先 stopSong()?
  5. 一首唱完,板子怎么知道"该自动接下一首"?
  6. 进度条怎么知道"现在第几秒、总长多少"?

留下三样东西:你 TF 卡里歌的总数、你点"下一首"后停在的那个下标(第几首)、"歌单在板子里是什么"的一句话回答。

十、还能玩什么

作品方向描述给 AI 的效果再想想
随机播放打乱顺序随机挑一首顺序是"下标+1",随机是另一种下标取法?
单曲/整单循环单曲循环,或整单播完再从头取模已让最后一首绕回第一首,单曲循环改哪?
图片封面每首歌配一张封面图歌名存数组,图片也能存吗?
滚动歌词边放边把歌词滚出来进度条在读播放时间,歌词怎么跟着时间走?
收藏/歌单编辑收藏、删歌、建多个列表"一个数组"记一首,那"很多个列表"记什么?

下节课预告:学会"把一堆文件整理成可点的歌单、用下标在歌单里挪"后,第 39 课让板子不光放声音,还能自己看图讲完整的故事——做一本"我的电子绘本"。你会发现,"声音会放"和"画面会换"是同一套"点一下、翻到下一页"的本领。

第 38 课 · TF 卡音乐播放器

第 38 课 · TF 卡音乐播放器

把 TF 卡里的 mp3/wav 扫成歌单显示在屏幕上,点歌播放,还能切歌、暂停和调音量。

38音频TF卡播放器