3. 波形合成基础:正弦波、方波、锯齿波、三角波的数学表达与Python生成

各位同学,欢迎来到波形合成的世界。

说实话,我当年刚接触音频编程时,觉得波形这东西挺玄乎的。不就是一条线在跳吗?后来在做一个8-bit风格的游戏音效时,我才真正体会到——波形就是声音的DNA。你选什么波形,直接决定了你的音色是“噗噗噗”还是“滴滴滴”。

这一章,咱们就把四种最基础的波形掰开揉碎。数学公式我会给,但更重要的是——怎么用Python把它们“画”出来,并且能听到

3.1 正弦波:最纯净的声音

正弦波是所有波形的基础。你想想看,自然界里几乎所有的声音,都可以分解成不同频率的正弦波叠加。这就是傅里叶变换的核心思想。

数学表达式:

y(t) = A * sin(2 * π * f * t + φ)
  • A:振幅,决定了音量大小。取值范围通常在0.0到1.0之间。
  • f:频率,单位Hz。比如440Hz就是标准A4音。
  • t:时间,单位秒。
  • φ:初始相位,一般取0就行。

重要概念:正弦波只有基频,没有泛音。所以听起来很“干净”,甚至有点无聊。但它是合成其他波形的基础。

Python生成示例:

import numpy as np
import matplotlib.pyplot as plt

# 参数设置
sample_rate = 44100  # 采样率,CD音质标准
duration = 0.02      # 只生成20ms,方便观察波形
freq = 440           # 频率440Hz
amplitude = 0.8      # 振幅

# 生成时间轴
t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)

# 生成正弦波
sine_wave = amplitude * np.sin(2 * np.pi * freq * t)

# 画出来看看
plt.plot(t[:200], sine_wave[:200])  # 只显示前200个点
plt.title('正弦波 (440Hz)')
plt.xlabel('时间 (秒)')
plt.ylabel('振幅')
plt.grid(True)
plt.show()

我的小经验:我在做游戏里“获得道具”的音效时,经常用正弦波做基础音。因为它干净,后期加混响和延迟效果特别好。但如果你直接拿正弦波做打击音效,那效果就有点“塑料”了。

3.2 方波:复古游戏机的灵魂

方波长什么样?说白了就是“高-低-高-低”来回跳。你小时候玩的红白机(FC),里面大部分音效都是方波。

数学表达式:

y(t) = A * sign(sin(2 * π * f * t))

其中 sign() 是符号函数:输入大于0输出1,小于0输出-1。

方波的特点:

  • 只包含奇次谐波(1f, 3f, 5f, 7f...)
  • 谐波幅度按 1/n 衰减
  • 听起来“嗡嗡”的,很有穿透力

Python生成示例:

# 生成方波:直接用符号函数
square_wave = amplitude * np.sign(np.sin(2 * np.pi * freq * t))

# 或者用numpy内置函数
square_wave = amplitude * np.square(2 * np.pi * freq * t)

plt.plot(t[:200], square_wave[:200])
plt.title('方波 (440Hz)')
plt.ylim(-1.2, 1.2)
plt.grid(True)
plt.show()

注意:方波在跳变沿处会产生高频分量。如果你直接播放,可能会听到“嘶嘶”的噪声。我建议在生成后加一个低通滤波器,把20kHz以上的分量切掉。这个后面章节会讲。

3.3 锯齿波:扫频音效的利器

锯齿波的形状像一把锯子的齿。它从最小值线性上升到最大值,然后瞬间跳回最小值。

数学表达式:

y(t) = 2 * A * (f * t - floor(f * t + 0.5))

或者更直观的写法:

y(t) = 2 * A * (t / T - floor(t / T + 0.5))

其中 T = 1/f 是周期。

锯齿波的特点:

  • 包含所有谐波(1f, 2f, 3f, 4f...)
  • 谐波幅度按 1/n 衰减
  • 听起来“明亮”、“尖锐”,适合做扫频音效

Python生成示例:

# 生成锯齿波
saw_wave = 2 * amplitude * (freq * t - np.floor(freq * t + 0.5))

plt.plot(t[:200], saw_wave[:200])
plt.title('锯齿波 (440Hz)')
plt.ylim(-1.2, 1.2)
plt.grid(True)
plt.show()

我曾经踩过的坑:有一次做游戏里“激光枪”的音效,我直接用锯齿波扫频。结果听起来像一只愤怒的蜜蜂。后来我加了一个包络(音量渐弱),效果立马就对了。记住:波形决定音色,包络决定感觉

3.4 三角波:温和的中间派

三角波看起来像锯齿波的“温和版”。它也是线性上升和下降,但不会瞬间跳变。

数学表达式:

y(t) = 2 * A * |2 * (f * t - floor(f * t + 0.5))| - A

嗯,这个公式看着有点绕。我更喜欢用分段函数来理解:

# 伪代码
phase = f * t - floor(f * t)  # 相位,范围0~1
if phase < 0.5:
    y = 4 * A * phase - A      # 上升段
else:
    y = -4 * A * phase + 3 * A # 下降段

三角波的特点:

  • 只包含奇次谐波(1f, 3f, 5f, 7f...)
  • 谐波幅度按 1/n² 衰减(比方波衰减更快)
  • 听起来“圆润”、“柔和”,适合做贝斯音色

Python生成示例:

# 生成三角波
phase = freq * t - np.floor(freq * t)
triangle_wave = np.where(phase < 0.5, 
                         4 * amplitude * phase - amplitude,
                         -4 * amplitude * phase + 3 * amplitude)

plt.plot(t[:200], triangle_wave[:200])
plt.title('三角波 (440Hz)')
plt.ylim(-1.2, 1.2)
plt.grid(True)
plt.show()

3.5 四种波形对比总结

为了方便你快速查阅,我把四种波形的核心参数整理成了表格:

波形类型 谐波成分 衰减速度 听感特点 典型用途
正弦波 仅基频 纯净、柔和 基础音、测试信号
方波 奇次谐波 1/n 嗡嗡、有穿透力 复古游戏音效、主旋律
锯齿波 所有谐波 1/n 明亮、尖锐 扫频音效、弦乐合成
三角波 奇次谐波 1/n² 圆润、温和 贝斯音色、低音铺垫

核心要点:谐波成分越丰富,音色越“复杂”。正弦波最简单,锯齿波最丰富。方波和三角波虽然都只有奇次谐波,但三角波的高频衰减更快,所以听起来更“暗”。

3.6 动手实践:生成并播放你的第一个波形

光看波形图不过瘾,咱们来听一听。下面这段代码会生成四种波形,并用电脑扬声器播放出来。

import sounddevice as sd

# 生成1秒的波形
duration = 1.0
t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)

# 四种波形
sine = amplitude * np.sin(2 * np.pi * freq * t)
square = amplitude * np.sign(np.sin(2 * np.pi * freq * t))
saw = 2 * amplitude * (freq * t - np.floor(freq * t + 0.5))
phase = freq * t - np.floor(freq * t)
triangle = np.where(phase < 0.5,
                    4 * amplitude * phase - amplitude,
                    -4 * amplitude * phase + 3 * amplitude)

# 逐个播放(注意音量,别吓到自己)
print("播放正弦波...")
sd.play(sine, sample_rate)
sd.wait()

print("播放方波...")
sd.play(square, sample_rate)
sd.wait()

print("播放锯齿波...")
sd.play(saw, sample_rate)
sd.wait()

print("播放三角波...")
sd.play(triangle, sample_rate)
sd.wait()

安全提示:方波和锯齿波的高频分量可能让你的耳机“嘶嘶”响。建议先把音量调小,再慢慢加大。我年轻时有一次戴着耳机直接播放方波,耳朵疼了半天。

3.7 本章小结

好了,四种基础波形咱们都过了一遍。你可能会问:就这?四个简单的形状能做出什么好听的音效?

嗯,别急。这就像学画画——你刚学会了画直线、圆圈、三角形。看起来简单,但组合起来就能画出整个世界。下一章,我会教你如何把这些波形组合起来,加上包络和调制,做出真正的游戏音效。

记住:波形是颜料,合成是画笔,你的耳朵才是最终的裁判

课后练习:

  1. 修改代码中的频率,生成一个C大调音阶(261Hz, 293Hz, 329Hz, 349Hz, 392Hz, 440Hz, 493Hz, 523Hz)
  2. 尝试用方波生成一个“嘟嘟”的提示音(频率800Hz,持续0.1秒)
  3. 思考:为什么游戏里“获得金币”的音效通常用正弦波,而“敌人出现”的音效用锯齿波?

下一章见!