1. 先做一件事:让它开口
烧录 demo01_tts_player(需要 WiFi 和密钥)。屏幕上有四句话,点一下第一句——板子开口了。
先别激动,做三件事:
- 把屏幕底部那行小字抄下来:
字数 声音 合成 播放,四个数字一个都别漏;- 再点最长的句子,把这四个数字也抄下来;
- 对比两行:字多的句子,哪些数字变大了?
这四个数字就是今天的证据链:发出 N 个字 → 收回 N 字节声音 → 云端合成 N 毫秒 → 喇叭播放 N 毫秒。
任务 1:四句证据链表
| 句子 | 字数 | 预测(先写:字节和播放会怎么变) | 实测字节 | 实测播放 |
|---|---|---|---|---|
| 今天天气真好 | 6 | |||
| 你好,我是会说话的板子 | 11 | |||
| 床前明月光,疑是地上霜 | 12 | |||
| 现在是下午三点二十五分 | 11 |
2. 反着用上一课的账:字节能念多久
上一课算过:喇叭吃的数字,每秒 16000 个 × 每个 2 字节 = 每秒 32000 字节。上一课是"说了几秒 → 多少字节",这一课反过来:收回多少字节 → 能念几秒。
算一算(写在纸上): 收回 59200 字节 → 能念 59200 ÷ 32000 = ______ 秒? (答案:约 1.85 秒。对照你抄的实测"播放"数字——板子真的念了差不多这么久。字节就是声音的长度。)
再算一笔:收回 89280 字节能念多久?(答案:约 2.79 秒。)和实测对照,对得上吗?
3. 三步,文字变声音
你点的那一下,背后是一场下行接力。注意和上一课对比——同一条链子,方向反了:
| 步骤 | 谁在做 | 在哪里 | 干什么 |
|---|---|---|---|
| 1 誊写寄出 | 板子 | 板子上 | 文字誊写成安全字符,连同三个旋钮寄给百度 |
| 2 合成 | 百度 | 云端 | 把字做成一串声音数字,寄回来 |
| 3 喂喇叭 | 板子 | 板子上 | 声音数字存进 pcmBuffer,一勺一勺写进喇叭 |
两课对偶表(重点,考试就考这个感觉)
| 第 24 课(上行) | 第 25 课(下行) | |
|---|---|---|
| 你给板子什么 | 声音 | 文字 |
| 板子寄出什么 | 声音数字(base64 装信封) | 文字(誊写成安全字符) |
| 云端干什么 | 比对猜字 | 照字面做声音 |
| 寄回来什么 | 文字 | 声音数字(PCM) |
| pcmBuffer 的角色 | 装出去的仓库 | 收回来的仓库 |
| 会出什么错 | 猜错字 | 做砸生僻字/多音字 |
注意:全程没有人"念"了这句话。云端不理解句意,它照字面把声音做出来。
4. 喇叭只认数字:喂饭比喻
喇叭是个只认数字的婴儿:喂它数字,它就振动;给它文字,它不张嘴——它不认识字。所以板子要把云端寄回的声音数字一勺一勺喂给它,每次一小块、速度要稳,声音才不抖。
云端是照字面做菜的大厨:递字条、收回声音数字。它不理解菜谱(句意),只照字面做。
⚠️ 比喻的边界:字里面没有藏着声音。你没法从"床"这个字里解压出一段声音——文字和声音数字是两种东西,中间隔着云端的"制作"。 想通了这一点,两个现象就通了:
- 为什么必须联网? 声音要云端现做,本地没得解压。
- 为什么同一句话每次念得几乎一样? 大厨每次用同一套做法,不是放同一盘录音。
5. 动手做实验
实验一:五种声音(demo02)
烧录 demo02_voice_knob。固定念"床前明月光,疑是地上霜",拧旋钮,每次先预测再听:
| 旋钮组合 | 预测(和上一行比哪里变) | 实测听感 | 实测字节 |
|---|---|---|---|
| 女声 · 正常 · 正常 | |||
| 男声 · 正常 · 正常 | |||
| 女声 · 快 · 正常 | |||
| 女声 · 慢 · 正常 | |||
| 女声 · 正常 · 高 |
盯住第三、四行的字节:同一句 12 个字,念得快,字节数明显变少。念得快 = 声音短 = 字节少——上一课的账,这里用上了。
实验二:让它念没见过的话
把 demo01 的预置句子改成你自己编的怪话(比如带生僻字的),重新烧录再点。念出来了?跳过了?念成别的音?把现象记下来。如果喇叭里预先藏了句子,它不可能"没见过"。
实验三:拔掉 WiFi(拆台实验)
板子有电、有喇叭、句子就在屏幕上——断掉 WiFi,点句子。
| 观察 | 推测 |
|---|---|
| 屏幕出现什么?喇叭响了吗? | |
| 为什么不响? |
做完把网连回去(不然 demo03 也做不了)。
6. 想自己看懂一点点代码:两个名字就够
打开 demo01_tts_player.ino(或问 AI 帮手),只看两个名字:
- textToSpeech:寄字换声音的那一棒。誊写文字 → 连同三个旋钮寄给百度 → 收回声音数字交给仓库。
- pcmBuffer:声音数字的家。上一课装出去换文字,这一课收回来喂喇叭——同一个仓库,一课出一课进。
中文伪代码(不用背):
点一下句子:
数一数有几个字
textToSpeech():
文字誊写成安全字符(urlEncode)
连同 per 发音人 / spd 语速 / pit 音调 寄给百度
收到声音数字,存进 pcmBuffer
playPcm():
一勺一勺写进喇叭(每秒 16000 个)
喂完多等一小会,让喇叭咽下最后一口
屏幕报告:字数 声音B 合成ms 播放ms
7. 把两课连起来:会说话的便签(demo03)
烧录 demo03_talking_notes。点底部按钮说 1~3 秒,你的话变成文字便签(24 课);点任意一条便签,板子把它念出来(25 课)。
试一试:录一句"下课去打球",然后点它——你说的和它念的,走的是同一个云端、两个相反的方向。这就是第 28 课语音助手的雏形:听懂你 → 想一想 → 说出来。
💡 demo03 里便签还是存在内存里,断电就丢。想不丢?把文字写进 TF 卡(第 10/14 课学过),这就是挑战任务第三题。
8. 换个思路:把"念"搬进板子(demo04 本地 TTS)
前面几节都在讲"念在云端"。现在反着来:把大厨请进板子。
烧录 demo04_zh_local_tts——它不需要 WiFi、不需要密钥,根本不联网。触摸屏幕,板子开口了;再拔一次网线试试:照样说话。
| 观察 | demo01 云端(刚才) | demo04 本地(现在) |
|---|---|---|
| 需要联网吗 | 必须 | 不需要 |
| 需要密钥吗 | 要 | 不要 |
| 拔掉 WiFi 点句子 | 不响 | 照样念 |
| 声音 | 更自然 | 略机械但清楚 |
为什么它不用网? 因为"合成"这一步就发生在板子自己身上:文字 → 板子里的"小嗓门"(ESP-TTS 引擎 + 烧在 flash 里的音色模型)→ 现场做出声音 → 喂喇叭。没有寄出、没有寄回,接力少了两棒。
云端 vs 本地:一张表说清
| 云端 TTS(demo01) | 本地 TTS(demo04) | |
|---|---|---|
| 合成在哪 | 百度云端 | 板子本地 |
| 需要什么 | WiFi + 百度 Key | 什么都不需要 |
| 声音质量 | 更自然、音色多(女/男声) | 略机械,内置小乐/小新等 4 个音色 |
| 升级换代 | 云端升级,板子跟着变好 | 板子出厂什么样就什么样 |
| 成本 | 要密钥、要流量 | 零成本、零依赖 |
| 一句话 | 板子 = 快递员 + 喇叭 | 板子自己就是大厨 |
⚠️ 但喇叭还是那个只认数字的婴儿:本地 TTS 同样要把声音数字一勺一勺喂给它。变的只是"声音从哪来"——一个在云端做、一个在板子做,做出来的都是同一串 PCM 数字。
实验四:串口说怪话
demo04 支持从电脑串口(115200)发一行中文,板子立刻念出来。试试:
- 让它念你的名字;
- 念一句"床前明月光"——和 demo02 云端念的比一比,哪个更像真人?
- 念一个生僻字——对比实验二的"云端大厨翻车",本地小嗓门也会翻车吗?
结论:云端 TTS 和本地 TTS 都会"做声音",只是大厨住址不同。选谁?要音质、要换音色 → 云端;要离线、要零成本、要隐私 → 本地。真实产品里两个常常搭配用。
9. 选读:为什么不要 MP3?
有人会问:手机里存的不都是 MP3 吗?为什么这课不用?
MP3 是压缩格式——把声音压小了省空间,但喇叭不认,必须先"解压"回原始数字才能播。解压要专门的解码库、要更多内存,对我们这块小板子是一笔大开支。
这课我们让云端直接寄回 PCM(喇叭认的原始数字),收来就能喂。代价是数据大:念 1 分钟的声音约 1.9MB。所以适合"念一句话",不适合"存一整首歌"。
一句话总结:MP3 省地方但要解压,PCM 占地方但开袋即食。 我们念的是短句,选开袋即食。
10. 自检小测(点开对答案)
1. 收回 59200 字节的声音,大约念多久?怎么算的?
约 1.85 秒。喇叭每秒吃 16000 个数字 × 2 字节 = 每秒 32000 字节,59200 ÷ 32000 ≈ 1.85。2. 文字变声音的三步里,哪步在云端?
第 2 步"合成":云端把文字做成声音数字。第 1 步誊写寄出和第 3 步喂喇叭都在板子上。3. 拔掉 WiFi 点句子,为什么不响?这说明了什么?
字条寄不出去,云端没做成声音。说明"念"发生在云端,板子只是快递员+喇叭;字里面没有藏着声音,本地没得解压。4. pcmBuffer 在 24 课和 25 课里各干什么?
24 课:装出去的仓库——你的声音数字从这里寄出去换文字。25 课:收回来的仓库——云端寄回的声音数字住在这里,等着喂给喇叭。方向相反,同一个仓库。5. textToSpeech 寄出去的是什么?捎带了哪三个旋钮?
寄出去的是誊写过的文字;捎带 per(发音人:女声/男声)、spd(语速:慢/正常/快)、pit(音调:低/正常/高)。6. 同一句话,为什么"快"档收回的字节数明显少?
念得快 = 声音更短 = 数字更少。字节就是声音的长度。7. 生僻字念不出来,是板子坏了吗?
不是。云端大厨没学过这个字,做不出来。换常用字能正常念,就说明链路是好的。8. demo04 本地 TTS 和 demo01 云端 TTS,最大的区别是什么?
合成发生在哪。demo01 在百度云端做声音(要联网+密钥);demo04 在板子本地做声音(ESP-TTS 引擎 + flash 里的音色模型),不需要任何网络和密钥。9. 拔掉网线,demo01 和 demo04 分别会怎样?为什么?
demo01 哑了——字条寄不出去,云端做不了声音。demo04 照样念——合成在板子自己身上,跟网络没关系。这正是"本地 AI vs 云端 AI"的分水岭。10. 本地 TTS 也是把声音一勺一勺喂给喇叭吗?
是。喇叭永远只认 PCM 数字,不管声音是云端做的还是本地做的,都得一勺一勺喂。变的只是"声音从哪来"。11. 挑战任务
- 字节预言家:在 demo01 里挑两句长度不同的句子,先在纸上算出它们各收回多少字节(提示:先估念的秒数 × 32000),再点开对照,看误差多大。
- 错误收藏家:故意找 3 句会让大厨"翻车"的话(生僻字 / 多音字 / 中英夹杂),把每句的翻车现象记下来,并归因。再拿同样的 3 句喂 demo04,比较"云端大厨"和"本地小嗓门"谁翻车翻得更有意思。
- 断电不丢的便签:给 demo03 想一个办法,让便签重启后还在(提示:第 10/14 课 TF 卡),把思路写给 AI 帮手看。
- 离线版语音助手(挑战):把 demo04 的"串口说话"接到第 23 课的本地大模型回答上——不联网,板子"听懂→想一想→说出来"全本地完成。这才是真·离线 AI。