The lesson text is only available in Chinese for now. Lesson titles, introductions, menus and buttons are shown in English.

Lesson 1023 lessons in totalAbout 9 minIncludes an interactive lab

How does a computer find which high and low components a single curve is made of?

The previous four lessons asked everything you can ask by looking at the curve alone. But some things it can never answer: press three piano keys at once and the microphone records just one rising and falling curve — nothing in the waveform shows that there are three notes inside. This lesson changes the question: not how the signal rises and falls over time, but which high and low components it is made of. The method is surprisingly plain: take a wave of known frequency, multiply it with the signal point by point, and average. If they match, the average isn’t zero; if they don’t, the positives and negatives cancel out to zero. Try every frequency, and the curve you get is the spectrum.

导读: 前四课把「只看那条曲线」能问的都问完了。可有些事它永远答不了——同时按下钢琴的三个键,麦克风只记下一条起伏的曲线,波形上完全看不出里面有三个音。这一课换个角度:不问它随时间怎么起伏,问它由哪些高低成分组成。 做法出乎意料地朴素:拿一支已知频率的波去和它逐点相乘,再求平均。 对上了平均值不为零,对不上正负抵消成零。把所有频率都试一遍,得到的那条曲线就是频谱。

第 10 课属于第 2 组「把录音变成可计算片段」,本组共 5 课

目标:把一个复杂声音拆成它的频率成分

第 02 课说过一句话:很多复杂声音都可以用一组高低、大小、起点各不相同的正弦波叠加出来。(正弦波就是那种最简单的、光滑匀速上下的曲线,听起来像很干净的电子提示音;频率指它一秒重复多少次,决定听起来有多高。)

现在要做的是反过来:给你叠加好的结果,把它拆回去——说出里面有哪些频率、每个有多强。

两条正弦逐点相加,得到一条复杂波形

图 1:440 Hz 幅度 1.0 的一条,880 Hz 幅度 0.5 的一条,逐点相加得到下面那条。麦克风记下的只有下面那条——上面两条得靠算法找回来。

做这件事的方法叫傅里叶变换。它把声音从时间域(横轴是时间)换到频率域(横轴是频率)。

更深一点的直觉:拿正弦去比

整套方法可以浓缩成一句话:

拿各种频率的正弦波去和信号比较。每一个频率都得到一个强度和一个相位。强度高,说明这个信号和这支正弦很像。

「比较」具体怎么比?逐点相乘,然后求平均。

  • 频率对上了:信号的波峰和试探波的波峰同时出现,乘出来大多是正数,平均下来是个不为零的数。
  • 频率对不上:两者时快时慢地错开,乘出来正负各半,平均下来互相抵消,结果接近零。

第 1 步:拿三个频率试一试

造一个已知答案的信号:440 Hz 幅度 1.0 加 880 Hz 幅度 0.5。然后拿三支试探波去乘:

用 440 Hz 试探 -> +1.0000
用 500 Hz 试探 -> -0.0000
用 880 Hz 试探 -> +0.5000
(都是相乘求平均再乘 2)
同频与不同频试探波相乘后的平均结果

图 2:上排灰线是原信号、彩线是试探波;下排是两者逐点相乘的结果,填色是它围出的面积。左边正的面积远多于负的,平均出来 1.0000;右边正负一样多,抵消成 0。

所以呢? 三个数一个不差:

  • 440 得到 1.0000——正是它在信号里的幅度。
  • 880 得到 0.5000——也对上了。
  • 500 得到 0.0000——信号里根本没有这个频率。

这个方法不只告诉你「有没有」,还直接给出「有多强」。 那个乘 2 就是为此加的:让读数直接等于幅度,不用再换算。

交互程序加载中……

第 2 步:把所有频率都试一遍

一个频率试完了,那就从低到高全试一遍:

一共试了 951 个频率
  最高的两处:  440 Hz  读数 1.0000
  最高的两处:  880 Hz  读数 0.5000
其余 949 个频率的读数最大只有 0.0000
试探频率从 100 扫到 2000 Hz 得到的频谱

图 3:横轴是「我拿哪个频率去问」,纵轴是「问到的强度有多大」。这条曲线就是频谱。

951 个频率里,949 个的读数是 0.0000,只有两个不是。 这两个峰的高度 1.00 和 0.50,正好是合成时两支正弦的幅度——不是画上去的,是算出来的。

第 3 步:只把起点挪一下,一支试探波就失灵了

上面有个隐患。把同一个信号的起点挪四分之一圈(正弦换成余弦),声音本身完全没变——含有的频率和强度一模一样,均方根都是 0.7906,只有起点不同。

再用同一支正弦试探波去问 440 Hz:

原信号            +1.0000
挪过起点的        -0.0000

读数掉到了 0。

不是这段声音里没有 440 Hz ——它明明还在,幅度还是 1.0。是试探波和它错开了四分之一圈:一个在波峰时另一个正好在零点,乘出来正负恰好抵光。

单支试探波会被起点位置抹掉,两支合起来则不会

图 4:左边只用一支试探波——挪过起点后整条读数塌了。右边用两支相差四分之一圈的,两种起点都稳定给出 1.0000。

解决办法:用两支,相差四分之一圈

一支正弦、一支余弦,两个结果取直角三角形的斜边:

用 记正弦那支的读数、 记余弦那支的读数,强度就是

换成两支取直角边长:1.0000 和 1.0000
两支合起来,起点挪到哪里读数都一样

而且两支还顺带给出了起点位置:

原信号     +1.5708 弧度
挪过起点的 -0.0000 弧度
差 1.5708,正好四分之一圈

1.5708 就是 。两支试探波同时给出了「有多强」和「从哪里起步」两个数——后者就是第 02 课提过的相位。

所以呢? 整套过程可以分成三步:

  1. 选一个频率
  2. 调整相位,找到让面积最大的那个起点
  3. 算出强度

用两支相差四分之一圈的试探波,第 2 步和第 3 步一次就做完了。至于为什么两支就够、为什么后面要写成复数,是第 11 课的事。

交互程序加载中……

第 4 步:能拆开,就能拼回去

如果频谱真的完整记下了这个信号,那只用扫出来的那几个成分,应该能把原波形拼回来。

只用 440 和 880 两个频率重建
逐点最大误差 2.11e-15

误差量级 ,是浮点运算的残渣。

所以呢? 这个信号本来就只由这两支波组成,所以能完全拼回来。这件事很重要,它说明:频谱不是随便挑出的几项摘要,它和原波形装着同样多的信息。

  • 从波形算出频谱,叫傅里叶变换。
  • 从频谱拼回波形,叫逆傅里叶变换。
  • 拿一组正弦按各自的强度和起点叠出想要的声音,这种做法叫加法合成。

第 5 步:换成真实的钢琴录音

合成信号太干净了。同一套试探,用在真实的 piano_c.wav 上:

1 秒钢琴录音,扫 100–2000 Hz
最高点 527 Hz,读数 0.1819
  1 倍   527 Hz  0.1819
  2 倍  1054 Hz  0.0251
  3 倍  1581 Hz  0.0012
真实钢琴录音扫频得到的频谱,主峰在 527 Hz

图 5:真实录音的频谱不像合成信号那样只有两根干净的线——峰有宽度,峰之间也不是零。但主峰仍然清清楚楚。

527 Hz 离标准的 C5(523 Hz)很近——这段录音弹的就是这个音。而 2 倍处还有主峰 14% 的读数,3 倍处几乎没有。

这些整数倍就是第 03 课讲过的泛音。 那一课说「同一个音高、不同乐器听起来不一样,差别在泛音分布上」——现在你有了一个能把泛音分布真的量出来的工具。

一个必须交代的局限:时间没了

试探这套办法有个代价。看这个实验:造两段声音,一段先低后高,一段先高后低,成分完全一样只是顺序相反。整段各扫一次:

              440 Hz    880 Hz
先低后高       0.5000    0.5000
先高后低       0.5000    0.5000

读数一模一样。

整段做一次变换会丢掉时间信息,分帧后逐帧变换才能找回来

图 6:整段做一次,「先低后高」和「先高后低」给出同一条频谱;先分帧再逐帧变换,顺序就回来了。

所以呢? 时间信息在**「求平均」那一步**就被抹掉了——平均不在乎顺序,这正是第 05 课那个「加法不在乎顺序」的老结论。

怎么找回来? 用第 06 课那条频域流水线:先分帧,再对每一帧各做一次变换。 那就是第 15 课的短时傅里叶变换,把每一帧的结果排成一列,就得到声谱图。

这一课得到了什么,下一课接着讲什么

傅里叶变换不是把声音「变成另一种东西」的魔法。 它做的事从头到尾只有一件:拿一组已知频率去和信号比较,记下每个频率匹配得有多强、从哪里起步。

四个实测结论:

  • 440 和 880 的读数是 1.0000 和 0.5000,正好是合成时的幅度。
  • 951 个试探频率里 949 个读数为 0——没有的频率真的读不出来。
  • 只用两个成分重建原波形,逐点最大误差 2.11×10⁻¹⁵:频谱和波形装着同样多的信息。
  • 真实钢琴录音主峰 527 Hz,2 倍处还有主峰的 14%——泛音第一次被量了出来。

以及一个悬而未决的问题:一支试探波会被起点位置抹掉,得用两支。 这两支为什么正好是「相差四分之一圈」的一对?为什么把它们写成一个数会更方便?

下一课回答这个:为什么描述一次振动需要两个数,以及为什么把这两个数装进一个复数会让后面所有公式都变简单。