你好呀,我是吴老师。
前两课你存的都是文字和图片。今天要存的,是声音。
先告诉你一个可能和你想的不一样的"真相":
板子听不见声音。
它收到的是一串数字。
不信?这一课你自己把它量出来。
读完之后,你应该能:说出板子的"耳朵"在哪儿;录一段、放出来;把声音存进文件;把声音变成一个数,再用这个数做出吹气球。
用时:约 50 分钟。准备:板子、数据线。
一、板子的耳朵,是一个小孔
先看看你的板子。

板子正面上方,印着「MIC」的那一小块 —— 那就是它的耳朵。
| 你有的 | 板子上的 |
|---|---|
| 耳朵 | 麦克风(话筒),就是那个小孔 |
| 嘴巴 | 喇叭(下一课讲) |
💡 它和你一样:耳朵负责听进来,嘴巴负责说出去。 只不过它听进来的不是"声音",是一串数字 —— 这就是今天要弄明白的事。
做个实验:
对着那个小孔说一句话,或者吹一口气。
它有任何反应吗?
……没有。屏幕上什么都没变。
⭐ 这就是重点:耳朵听到了,但它不会自动做任何事。 听见 ≠ 有用。得你写程序告诉它"听到之后要干嘛" —— 这正是这一课要干的活。
二、它听到的,其实是一串数字
对着小孔说话的时候,音量一会儿大一会儿小。
板子干的事,就是每隔一小会儿量一下"现在有多响",记一个数。
这些数连起来,就是你刚才说的那句话。
💡 这就像用一支很快的笔,把你说话的音量画成一条曲线。 曲线画得越密,听起来就越像。
有个数你要记住:
每秒量 16000 次 (就是 audio.RATE)
每次记 2 个字节
→ 1 秒的录音 = 32000 字节 = 32 KB
⭐ 这个 32KB 待会儿算账要用,先记住。
三、录一段,放出来
录一段,只要一行:
pcm = audio.record(2000) # 录 2000 毫秒 = 2 秒
放出来,也只要一行:
audio.play_pcm(pcm)
就这么多。 你不用告诉它"录了多久""什么格式" —— 它自己知道。
⚠️ 录一次最多 10 秒(10000 毫秒)。再多它会直接报错, 因为录下来的东西要先整块放进内存,10 秒就是 320KB。
💡
record()是"堵着"的:说好录 2 秒,这 2 秒里程序就停在那儿等 —— 屏幕上画不了东西,触摸也读不到。所以录音时间别设太长, 不然别人会以为板子卡死了。
做个实验:把速度弄错一次。
让 AI 把播放那行改成这样:
audio.play_pcm(pcm, 32000) # 故意传一个别的速度
再放一遍刚才录的话。
听到什么了? —— 声音变得又快又尖,像花栗鼠。
⭐ 为什么? 录的时候是每秒 16000 个点,放的时候却按每秒 32000 个点来放 —— 同样的数字,放快了一倍,声音就尖了一倍。 就像录像带快放会变声一样。
亲耳听一次,比看十遍文字记得牢。 试完记得改回来。
四、声音也能存进文件
好消息:声音本来就是一串数字,而文件里存的也是数字 —— 所以直接写进去就行,不用做任何转换。
with open(FILE, 'wb') as f: # ⚠️ 注意是 'wb'
f.write(pcm)
with open(FILE, 'rb') as f: # 读回来
pcm = f.read()
⚠️⚠️ 多了个
b,千万别漏。
写法 干什么 你之前在哪儿用过 'w'/'r'读文字 名字、分数(第 10 课) 'wb'/'rb'读字节 声音(这一课)、图片(第 11 课) 用
'w'去写声音会报错 —— 它以为你在写"字",可声音不是字。
五、⭐ 声音能变成「一个数」
前面都是"把声音存起来"。现在讲这一课最值钱的一件事:
把一整段声音,压缩成"有多响"这一个数。
为什么要这么干?因为一个数才好用:
- 一个数可以比大小 → "现在比我刚才更响吗?"
- 一个数可以当门槛 → "响过这个数,就说明有人在吹"
- 一个数可以画成条 → 屏幕底下那根会动的力气条
怎么把一串数变成一个数?取平均。
import struct
def loudness(pcm):
n = len(pcm) // 2
s = struct.unpack('<%dh' % n, pcm) # 把字节变成一串整数
return sum(map(abs, s)) // len(s) # 取绝对值、加起来、除以个数
你不用会写这三行 —— 你已经会"许愿"了,让 AI 写就行。 但你要看得懂它在干嘛:
| 那一行 | 在干嘛 |
|---|---|
struct.unpack | 把一串字节翻译成一串整数(第 11 课的图片也是这么干的) |
abs(...) | 只要大小,不管正负 —— 声音的波形有正有负 |
sum(...) // len(s) | 全部加起来,再除以个数 —— 就是平均 |
做个实验:
让 AI 写一小段程序,在屏幕上显示这个数,然后:
- 安静的时候看一次 → 记下这个数
- 对着小孔大声说话 → 再看一次
- 对着小孔用力吹气 → 再看一次
数字是不是一次比一次大?
⭐ 这就是"声音大小"在板子里的样子 —— 一个数。 记住了,下一节就用它。
⚠️ 有个小坑,是真踩过的:录下来的头几个点永远是脏的。 实测第一条数据经常是个特别大的怪数(比如 16383),跟你的声音一点关系都没有。 所以上面那行的
[8:]意思就是:开头 8 个点,不要了。💡 这种坑不会报错,只会让数字莫名其妙 —— 所以一定要眼见为实。
六、⭐⭐ 做一个吹气球
现在把上面所有东西拼起来:让声音控制画面。
玩法:对着小孔吹气 → 气球变大 → 停下就慢慢漏气 → 吹太猛,砰!

规则只有一条:
现在的数字 > 门槛 → 气球长大一点;否则变小一点。
听起来很简单,但中间有两个坑,不解决就一点都不好玩。
坑一:门槛不能写死
做个实验:先让 AI 把门槛写成一个死数(比如 100),然后:
- 在安静的教室里运行 → 好像还行
- 让对方在旁边大声说话 → 气球自己一直长大,根本不用吹
为什么? 因为每间教室"安静"的标准不一样。安静教室的底噪是 40,热闹教室是 90 —— 用同一个死数,总有一边不对劲。
解法:开机先"听一下"。
开机的头半秒,板子什么也不干,只听 —— 量出"这里安静的时候有多响",再拿它当尺子:
quiet = calibrate() # 先听半秒
threshold = quiet * 2 + 80 # 门槛 = 比安静时响一截
⭐ 这个办法叫"先校准",真仪器也是这么干的 —— 电子秤要先归零, 体温枪要先测室温。先在安静的时候量一次,再拿它当尺子。
坑二:那个数会一跳一跳的
再做个实验:让 AI 把"现在的数字"直接显示在屏幕上,然后保持安静,盯着看十秒。
它是不是在乱跳? 一会儿 30,一会儿 200,偶尔还蹦到 260。
如果直接拿它当判据 → 气球会一抖一抖的,甚至莫名其妙自己长大。
解法:平滑 —— 让这个数"慢慢跟过去",别一步跳到位。
level = level + (now - level) / 2 # 只往新的数挪一半过去
💡 想象你端着一杯水走路:慢慢挪,水不会洒;猛地一晃,全洒了。 声音数字也一样,猛跳的那一下多半是错的。
拼起来

| 屏幕上的东西 | 是什么 |
|---|---|
| 气球 | 吹得够久,它就越大 |
| 绿色力气条 | 现在有多响 —— 就是第五节那个数,实时画出来 |
| 橙色竖线 | 门槛 —— 绿条冲过橙线,才算"在吹" |
吹到快满:

吹过头:

🎉 恭喜,你已经会"用声音控制东西"了。 这件事听起来很高级,其实核心就是第五节那五个字:变成一个数。
⚠️ 写这个游戏还要注意一件事:别整屏重画。 实测整屏推一次要 59 毫秒,而每 60 毫秒就要重画一次 —— 两样加起来一帧一百多毫秒,画面会一顿一顿的。 用
screen.flush_rect只推会变的两小块,加起来约 36 毫秒。 你让 AI 写的时候,直接把这句话告诉它就行。
吹不动怎么办?
| 现象 | 怎么办 |
|---|---|
| 怎么吹气球都不长 | 话筒灵敏度调高(MIC_GAIN,最大 42) |
| 不用吹气球自己就长大 | 门槛调高;或者运到安静一点的地方再开机(开机那半秒它要听底噪) |
| 气球一抖一抖的 | 门槛调高一点,平滑的力度调大 |
七、算一笔账:能存几条?
还记得板子上能存东西的地方一共 7.9 MB 吗?
| 一条多长 | 要多少空间 | 能存几条 |
|---|---|---|
| 2 秒 | 64 KB | 约 120 条 |
| 5 秒 | 160 KB | 约 48 条 |
| 10 秒(录满) | 320 KB | 约 24 条 |
⭐ 又是那个规律:时间长一倍,占的空间也大一倍。
所以"声音便条"这个主意怎么样? 录 2 秒存一条,能存一百多条 —— 完全够用。
但如果你想做"整首歌听下来"(3 分钟),那是 3×60×32KB ≈ 5.6 MB —— 一首歌就快把板子塞满了。
💡 这就是为什么板子上不能放音乐:不是它不会放,是存不下。 真正的音乐播放器要用"压缩"把文件变小,那是另一门本事了。
八、动手做
任务一:声音便条

请帮我做一个声音便条:
1. 左边一个按钮「录一条」:录 ___ 秒,存进文件
2. 右边一个按钮「放出来」:把存的那条播出来
3. 如果还没录过,点「放出来」要提示「还没录过呢」,不能报错
4. 关掉应用再打开,存的那条还在
5. 中文注释
做完请做三件事,一件都不能少:
- 录一条
- 退出应用
- 再打开,点「放出来」 —— 还能听到吗?
🎉 能听到的话,恭喜 —— 你会做留声机了。
任务二:把游戏改成你自己的
请帮我改一下吹气球,做成 ___________ :
1. 还是先用话筒量「现在有多响」那个数(开机先校准,别写死门槛)
2. 这次不用气球,改成 ___________(比如:一根上升的水柱 / 一只越鼓越大的青蛙 / 一栋越盖越高的楼)
3. 超过 ___ 就 ___________(比如:水溢出来 / 青蛙跳走 / 楼塌了)
4. 中文注释
做完请检查两件事:
- 安静的时候它自己动吗?(动了 = 门槛太低了)
- 大喊一声能不能推动它?(推不动 = 门槛太高,或者话筒太迟钝)
九、只改一个条件
挑一个来改,先说出你的预测,再动手:
| 我改的条件 | 我预测会怎样 | 实际怎样 |
|---|---|---|
| 录的时间从 2 秒改成 8 秒 | ||
| 录 11 秒 | ||
存的时候用 'w' 而不是 'wb' | ||
播的时候传个别的速度 audio.play_pcm(pcm, 32000) | ||
把门槛 threshold 减掉一半 | ||
| 把开机的"听半秒"删掉,门槛写死 100 |
💡 最后两个最值得做。 你会亲眼看到"校准"这件事不是多此一举 —— 换个教室,写死的门槛就不灵了。
十、自检小测
十一、还想玩点什么
| 想做什么 | 可以这样描述给 AI |
|---|---|
| 看谁喊得响 | 屏幕上画一根柱子,谁喊得响柱子冲得高,记下最高分 |
| 声音开关 | 拍一下手,屏幕上的灯就亮(超过门槛 = 拍手) |
| 会说话的相册 | 每张照片配一段录音,点照片就放那段 |
| 安静提醒器 | 教室里太吵就亮红灯,安静下来变绿 |
⭐ 下一课,我们要用板子的另一个器官:嘴巴。
你已经知道"声音进来是一串数字"。下一课你会发现 —— 声音出去,也是一串数字,只不过方向反过来。
等你两边都会了,就能做出会听也会说的东西了。


