噪声是芯片音乐中不可或缺的元素——鼓点、打击乐、爆炸音效、海浪声,都来自噪声发生器。本课我们用LFSR(线性反馈移位寄存器)实现硬件噪声生成,这是所有经典声音芯片使用的技术。
在音频领域,噪声是指随机或不规则的声波信号。与乐音(周期性波形)不同,噪声没有明确的音高,听起来像"嘶嘶"或"沙沙"声。
按频谱分布,噪声被形象地用"颜色"分类:
| 类型 | 频谱特征 | 听感 | 典型应用 |
|---|---|---|---|
| 白噪声 | 各频率能量相等 | 嘶嘶声,像收音机无信号 | 军鼓、踩镲 |
| 粉红噪声 | 能量按1/f衰减 | 更柔和,像雨声 | 环境音 |
| 棕噪声 | 能量按1/f²衰减 | 低沉轰鸣,像海浪 | 爆炸、雷声 |
| 蓝噪声 | 能量按f增长 | 尖锐嘶嘶 | 特殊效果 |
芯片音乐主要使用白噪声及其变种,因为LFSR天然产生白噪声。
线性反馈移位寄存器是芯片噪声生成的核心:
LFSR由一个移位寄存器和异或(XOR)反馈网络组成:
┌───┬───┬───┬───┬───┬───┬───┬───┐
│b15│b14│b13│b12│b11│...│ b1│ b0│
└─┬─┴─┬─┴───┴───┴───┴───┴───┴─┬─┘
│ │ │
└──XOR──┐ │
│ feedback │
└────────────────────┘
LFSR是"伪随机"的——序列看起来随机,但实际上是完全确定性的,最终会循环。好的LFSR配置可以产生非常长的周期(2^n - 1),16位LFSR最长周期为65535步。
抽头位置决定了LFSR的周期长度和统计特性:
| 模式 | 抽头位置 | 周期 | 音色特征 |
|---|---|---|---|
| 短序列 | [15,14] | 短 | 嗡嗡声,有调性 |
| NES风格 | [15,13] | 32767 | 经典白噪声 |
| 长1 | [15,14,12,10] | 65535 | 全周期白噪声 |
| 金属感 | [15,11] | 32767 | 金属质感,像镲片 |
噪声没有明确的音高,但移位速率影响噪声的"质感":
通过调节shift_rate参数,我们可以从同一LFSR获得各种质感的噪声。
rate_counter和shift_rate配合,控制LFSR移位的速率。shift_rate越大,移位越慢,噪声越粗糙。
7种模式对应7组抽头配置,通过noise_mode选择。case语句在综合时变成MUX。
复位时LFSR初始化为0xACE1(非零)。LFSR绝对不能全零——全零状态永远不会离开(0 XOR 0 = 0)。
取LFSR的高位作为输出。多位输出比单bit输出提供更多振幅变化,使噪声听起来更"自然"。
NES的鼓点全部来自噪声通道:
// 军鼓:短时高速噪声 + 三角波
// 踩镲:极短时高速噪声
// 底鼓:不使用噪声!用三角波下降音高模拟
// 速率参考(NES时钟约894886Hz):
// 高速 ≈ 每周期移位一次 → 白噪声(军鼓/踩镲)
// 中速 ≈ 每4周期移位 → 粗噪声(爆炸)
// 低速 ≈ 每16周期移位 → 嗡嗡声(引擎)
噪声大师 — 掌握LFSR伪随机数生成原理,实现可配置噪声发生器,理解噪声模式与音色的关系!四种基本波形全部完成!
// lfsr_noise.v - LFSR噪声发生器
// 支持7种噪声模式(不同反馈抽头配置)
module lfsr_noise #(
parameter BIT_DEPTH = 8,
parameter LFSR_BITS = 16 // LFSR位宽
)(
input wire clk,
input wire rst_n,
input wire enable,
input wire [2:0] noise_mode, // 噪声模式选择
input wire [15:0] shift_rate, // 移位速率控制
output wire [BIT_DEPTH-1:0] noise_out
);
reg [LFSR_BITS-1:0] lfsr;
reg [15:0] rate_counter;
// 移位速率分频器
wire shift_en;
assign shift_en = enable && (rate_counter >= shift_rate);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
rate_counter <= 16'd0;
end else if (enable) begin
if (rate_counter >= shift_rate)
rate_counter <= 16'd0;
else
rate_counter <= rate_counter + 16'd1;
end
end
// LFSR反馈位(异或抽头)
reg feedback;
always @(*) begin
case (noise_mode)
3'd0: feedback = lfsr[15] ^ lfsr[14]; // 短序列
3'd1: feedback = lfsr[15] ^ lfsr[13]; // NES风格
3'd2: feedback = lfsr[15] ^ lfsr[14] ^ lfsr[12] ^ lfsr[10]; // 长1
3'd3: feedback = lfsr[15] ^ lfsr[12]; // 长2
3'd4: feedback = lfsr[15] ^ lfsr[11]; // 金属感
3'd5: feedback = lfsr[15] ^ lfsr[14] ^ lfsr[7]; // 风格化
3'd6: feedback = lfsr[15] ^ lfsr[13] ^ lfsr[11]; // 软噪声
default: feedback = lfsr[15] ^ lfsr[14];
endcase
end
// LFSR移位寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
lfsr <= 16'hACE1; // 种子值(非零)
end else if (shift_en) begin
lfsr <= {lfsr[LFSR_BITS-2:0], feedback};
end
end
// 输出:将LFSR高位扩展为BIT_DEPTH位
// 使用LFSR的多个位产生多级噪声
assign noise_out = {lfsr[15:15-BIT_DEPTH+1]} & {BIT_DEPTH{enable}};
endmodule
✅ Verilator验证通过
LFSR的理论基础是有限域(Galois Field)理论和本原多项式(Primitive Polynomial):
一个n位LFSR要达到最大周期2^n-1,其反馈多项式必须是本原多项式。对于16位LFSR,本原多项式包括:
x^16 + x^15 + x^13 + x^4 + 1 → 抽头 [15, 13]
x^16 + x^14 + x^13 + x^11 + 1 → 抽头 [15, 14, 13, 11]
x^16 + x^15 + x^12 + x^10 + 1 → 抽头 [15, 14, 12, 10] ← 我们使用的
选择不同的本原多项式产生不同但等价的伪随机序列——周期都是65535,但具体序列不同。
NES 2A04噪声通道有两种模式,由寄存器位$400C bit 7控制:
| 模式 | 周期 | 频谱 | 听感 | 用途 |
|---|---|---|---|---|
| 长模式 | 32767步 | 接近白噪声 | 嘶嘶声,自然 | 军鼓、踩镲、风声 |
| 短模式 | 93步 | 有调性周期 | 嗡嗡声,金属 | 激光音效、引擎 |
短模式的93步周期产生约1.6kHz的基频(取决于移位速率),有明显的音高感。这个"缺陷"被巧妙地用作特殊音效。
单纯的LFSR噪声听起来很"干",需要处理才能变成有趣的鼓声:
最关键的处理——控制噪声的持续时间和衰减速率:
虽然我们没有实现完整的数字滤波器,但可以通过简单方法模拟:
最有效的鼓音色是噪声+音调的混合:
// 军鼓 = 噪声(高频) + 三角波(中频)
snare_out = (noise * noise_level + triangle * tone_level) * envelope;
// 底鼓 = 正弦波(频率扫描) + 轻微噪声
kick_out = (sine(freq_sweep) + noise * 0.1) * envelope;
除了音频,LFSR在密码学和通信中也有重要应用:
在芯片音乐中,经常混合不同特征的噪声产生更丰富的打击乐:
// 白噪声(高速LFSR) + 低频噪声(低速LFSR) = 有"轰鸣感"的军鼓
wire [7:0] white_noise = lfsr_high_speed[15:8];
wire [7:0] low_noise = lfsr_low_speed[15:8];
wire [7:0] mixed_noise = (white_noise + low_noise) >> 1;
// 噪声 + 低频正弦 = "有调性"的打击乐
wire [7:0] tonal_hit = (noise_out + sine_low_freq) >> 1;
// 多层噪声 = 更"自然"的打击乐
wire [7:0] natural = (lfsr1_out + lfsr2_out + lfsr3_out) / 3;
通用LFSR模块,可配置抽头和位宽:
module lfsr_generic #(
parameter WIDTH = 16,
parameter [WIDTH-1:0] TAPS = 16'b1100000000000000, // 抽头位置
parameter [WIDTH-1:0] SEED = 16'hACE1 // 初始种子(非零!)
)(
input wire clk,
input wire rst_n,
input wire enable,
output wire [WIDTH-1:0] data_out
);
reg [WIDTH-1:0] lfsr;
// 计算反馈位:所有抽头位异或
wire feedback;
assign feedback = ^(lfsr & TAPS); // 缩位异或
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
lfsr <= SEED; // 重要:种子不能为0!
else if (enable)
lfsr <= {lfsr[WIDTH-2:0], feedback};
end
assign data_out = lfsr;
endmodule
这个参数化模块通过修改TAP参数就能支持任意LFSR配置。缩位异或运算符^是Verilog的利器——它将所有位异或在一起,一行代码替代多行case。