AI脑洞板
完整教案
自学课件
第 12 课 · 共 35 课

麦克风 —— 板子的耳朵 板子的耳朵

你好呀,我是吴老师。

前两课你存的都是文字和图片。今天要存的,是声音。

先告诉你一个可能和你想的不一样的"真相":

板子听不见声音。

它收到的是一串数字。

不信?这一课你自己把它量出来。

读完之后,你应该能:说出板子的"耳朵"在哪儿;录一段、放出来;把声音存进文件;把声音变成一个数,再用这个数做出吹气球。

用时:约 50 分钟。准备:板子、数据线。


一、板子的耳朵,是一个小孔

先看看你的板子。

板子正面上方,印着 MIC 的那一小块,红圈里就是它的耳朵

板子正面上方,印着「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' 去写声音会报错 —— 它以为你在写"字",可声音不是字。

五、⭐ 声音能变成「一个数」

前面都是"把声音存起来"。现在讲这一课最值钱的一件事:

把一整段声音,压缩成"有多响"这一个数。

一段 60 毫秒的录音里有一千来个点,取平均之后只剩一个数

为什么要这么干?因为一个数才好用:

  • 一个数可以比大小 → "现在比我刚才更响吗?"
  • 一个数可以当门槛 → "响过这个数,就说明有人在吹"
  • 一个数可以画成条 → 屏幕底下那根会动的力气条

怎么把一串数变成一个数?取平均。

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 写一小段程序,在屏幕上显示这个数,然后:

  1. 安静的时候看一次 → 记下这个数
  2. 对着小孔大声说话 → 再看一次
  3. 对着小孔用力吹气 → 再看一次

数字是不是一次比一次大?

⭐ 这就是"声音大小"在板子里的样子 —— 一个数。 记住了,下一节就用它。

⚠️ 有个小坑,是真踩过的:录下来的头几个点永远是脏的。 实测第一条数据经常是个特别大的怪数(比如 16383),跟你的声音一点关系都没有。 所以上面那行的 [8:] 意思就是:开头 8 个点,不要了。

💡 这种坑不会报错,只会让数字莫名其妙 —— 所以一定要眼见为实。

六、⭐⭐ 做一个吹气球

现在把上面所有东西拼起来:让声音控制画面。

玩法:对着小孔吹气 → 气球变大 → 停下就慢慢漏气 → 吹太猛,砰!

刚打开的样子:气球小小的,力气条还没动

规则只有一条:

现在的数字 > 门槛 → 气球长大一点;否则变小一点。

听起来很简单,但中间有两个坑,不解决就一点都不好玩。

坑一:门槛不能写死

做个实验:先让 AI 把门槛写成一个死数(比如 100),然后:

  • 在安静的教室里运行 → 好像还行
  • 让对方在旁边大声说话 → 气球自己一直长大,根本不用吹

为什么? 因为每间教室"安静"的标准不一样。安静教室的底噪是 40,热闹教室是 90 —— 用同一个死数,总有一边不对劲。

解法:开机先"听一下"。

安静的教室门槛 160,热闹的教室门槛 260 —— 门槛得自己量

开机的头半秒,板子什么也不干,只听 —— 量出"这里安静的时候有多响",再拿它当尺子:

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. 再打开,点「放出来」 —— 还能听到吗?

🎉 能听到的话,恭喜 —— 你会做留声机了。

任务二:把游戏改成你自己的

请帮我改一下吹气球,做成 ___________ :
1. 还是先用话筒量「现在有多响」那个数(开机先校准,别写死门槛)
2. 这次不用气球,改成 ___________(比如:一根上升的水柱 / 一只越鼓越大的青蛙 / 一栋越盖越高的楼)
3. 超过 ___ 就 ___________(比如:水溢出来 / 青蛙跳走 / 楼塌了)
4. 中文注释

做完请检查两件事:

  1. 安静的时候它自己动吗?(动了 = 门槛太低了)
  2. 大喊一声能不能推动它?(推不动 = 门槛太高,或者话筒太迟钝)

九、只改一个条件

挑一个来改,先说出你的预测,再动手:

✏️ 可以直接在表格里填写
我改的条件我预测会怎样实际怎样
录的时间从 2 秒改成 8 秒
录 11 秒
存的时候用 'w' 而不是 'wb'
播的时候传个别的速度 audio.play_pcm(pcm, 32000)
把门槛 threshold 减掉一半
把开机的"听半秒"删掉,门槛写死 100

💡 最后两个最值得做。 你会亲眼看到"校准"这件事不是多此一举 —— 换个教室,写死的门槛就不灵了。

十、自检小测

正在加载本课自检小测…

十一、还想玩点什么

想做什么可以这样描述给 AI
看谁喊得响屏幕上画一根柱子,谁喊得响柱子冲得高,记下最高分
声音开关拍一下手,屏幕上的灯就亮(超过门槛 = 拍手)
会说话的相册每张照片配一段录音,点照片就放那段
安静提醒器教室里太吵就亮红灯,安静下来变绿

⭐ 下一课,我们要用板子的另一个器官:嘴巴。

你已经知道"声音进来是一串数字"。下一课你会发现 —— 声音出去,也是一串数字,只不过方向反过来。

等你两边都会了,就能做出会听也会说的东西了。

本节演示

录音机

录音机

点一下录 2 秒,马上放出来听

声音便条

声音便条

录一段存进板子,关掉再打开还能听

吹气球

吹气球

对着话筒吹气,气球越吹越大,吹太猛就爆