雨声不是"随机噪声"——它是经过精确滤波和调制的信号。本文揭示 E-Brufen 中四种自然音效背后的数学原理和算法实现。


一、为什么高斯噪声而非均匀噪声?

1.1 Box-Muller 变换

final _rand = Random();

double _gauss() {
  final u1 = _rand.nextDouble();  // [0, 1) 均匀分布
  final u2 = _rand.nextDouble();  // [0, 1) 均匀分布
  return sqrt(-2 * log(max(u1, 0.0001))) * cos(2 * pi * u2);
}

这是经典的 Box-Muller 变换——将两个独立的 [0,1) 均匀随机数转换为标准正态分布 N(0,1)。

1.2 为什么自然音效需要高斯分布?

均匀噪声 (Random.nextDouble)
├─ 频谱:所有频率等概率
├─ 听感:刺耳的"静电声"
└─ 原因:自然界不存在等概率的频谱分布

高斯噪声 (Box-Muller)
├─ 频谱:能量集中在均值附近
├─ 听感:柔和的"沙沙声"
└─ 原因:自然界的大多数随机过程遵循正态分布

★ Insight ─────────────────────────────────────
自然界的声音之所以"自然",是因为它们遵循中心极限定理——大量独立随机因素的叠加趋于正态分布。雨滴落下的声音是成千上万个小水滴的叠加 → 高斯分布。直接使用均匀分布的 Random.nextDouble() 生成的声音听起来"不自然",根因就在这里。
─────────────────────────────────────────────────


二、雨声:高通滤波 + 低频包络

2.1 算法流程

高斯白噪声 → 低通滤波(LPF) → raw - LPF×0.5 = 高通 → × 双正弦包络 → 雨声
                                                       × 随机"大水滴" (0.3%)

2.2 完整实现

在这里插入图片描述

List<int> _generateRain(String path, int sr, int durSec) {
  final n = sr * durSec;
  final samples = <int>[];
  double lpf = 0;  // 一阶低通滤波器记忆

  for (var i = 0; i < n; i++) {
    final t = i / sr;

    // 包络:两个不同频率正弦波的乘积 → 不规则的疏密变化
    final envelope = 0.6 + 0.4 * sin(2 * pi * 0.07 * t) * sin(2 * pi * 0.13 * t);

    final raw = _gauss();
    lpf = 0.3 * raw + 0.7 * lpf;   // 一阶 IIR 低通:y[n] = 0.3x[n] + 0.7y[n-1]
    final high = raw - lpf * 0.5;   // 高通 = 原始 - 低通分量

    var val = high * envelope * 0.5;

    // 偶尔的"大水滴"——模拟雨滴打在屋檐/窗户上的声音
    if (_rand.nextDouble() < 0.003) {
      val += _gauss() * 1.8;
    }

    samples.add((val * 18000).toInt());
  }
  _writeWav(path, sr, samples);
  return samples;
}

2.3 参数解析

参数 作用
lpf 系数 (0.3, 0.7) y[n] = 0.3x[n] + 0.7y[n-1] 截止频率约 1kHz
包络 sin(0.07t)×sin(0.13t) ~14s 和 ~7.7s 周期 "时大时小"的雨势变化
大水滴概率 0.3% 平均每秒 6-7 个突出滴声

三、海浪:布朗噪声(1/f² 频谱)

3.1 布朗噪声的数学定义

布朗噪声 = 白噪声的积分(累积和):

brown[n] = brown[n-1] + white[n] × 步长

频谱特性:power ∝ 1/f²,即频率每翻一倍,能量降为 1/4。这产生了极其低沉、隆隆的声音。

3.2 实现

List<int> _generateOcean(String path, int sr, int durSec) {
  final n = sr * durSec;
  final samples = <int>[];
  double brown = 0;

  for (var i = 0; i < n; i++) {
    final t = i / sr;

    brown += _gauss() * 0.02;       // 积分步长 = 0.02
    brown = brown.clamp(-3.0, 3.0);  // 防止数值漂移

    // 海浪包络:两个慢波相乘
    final wave1 = (sin(2 * pi * 0.08 * t) + 1) / 2;   // 12.5s 周期
    final wave2 = (sin(2 * pi * 0.05 * t + 1.5) + 1) / 2; // 20s 周期
    final envelope = 0.2 + 0.8 * wave1 * wave2;

    samples.add((brown * envelope * 0.7 * 25000).toInt());
  }
  _writeWav(path, sr, samples);
  return samples;
}

★ Insight ─────────────────────────────────────
包络使用两个不同频率、不同相位的正弦波相乘而非相加——这是关键。相加产生的波形规则可预测(“听几秒就知道下一个浪什么时候来”),而相乘产生不规则的复合波(“浪高浪低没有固定规律”)。真正的海浪正是如此——受月球引力、风速、海底地形等多因素影响,永远不会呈现完美的周期性。
─────────────────────────────────────────────────

3.3 参数分析

参数 效果
积分步长 0.02 控制"隆隆"的强度
clamp 范围 [-3.0, 3.0] 防止长时间积分漂移导致失真
wave1 频率 0.08 Hz 主浪周期 ~12.5 秒
wave2 频率 0.05 Hz 次浪周期 ~20 秒
相位偏移 1.5 rad 避免两波同步叠加

四、篝火:稀疏事件模型

4.1 三层声音结构

篝火声音模型
├─ 底层:持续低频底噪 (base)        → 火焰的"身体"
├─ 中层:4% 概率的噼啪脉冲 (2-22样本) → 小木柴爆裂
└─ 顶层:0.2% 概率的大爆裂 (2.5×振幅) → 大木块崩裂

4.2 实现

List<int> _generateFire(String path, int sr, int durSec) {
  final n = sr * durSec;
  final samples = <int>[];

  for (var i = 0; i < n; i++) {
    final base = _gauss() * 0.15;  // 持续低频底噪

    var crackle = 0.0;
    if (_rand.nextDouble() < 0.04) {  // 4% 噼啪概率
      final crackLen = 2 + _rand.nextInt(20);    // 2-22 样本
      final crackAmp = 0.3 + _rand.nextDouble() * 1.2;
      for (var j = 0; j < crackLen && i + j < n; j++) {
        final decay = 1 - (j / crackLen);
        samples.add(((base + crackAmp * decay * (_gauss() * 2)) * 14000).toInt());
      }
      i += crackLen;  // 跳过已生成的样本
      continue;
    }

    // 偶尔的大块爆裂
    if (_rand.nextDouble() < 0.002) {
      crackle = _gauss() * 2.5;
    }

    samples.add(((base + crackle) * 0.8 * 18000).toInt());
  }
  return samples;
}

4.3 稀疏事件模型的关键参数

参数 含义 调大效果
0.04 噼啪概率 更"活跃"的篝火
2-22 样本 噼啪持续时长 噼啪声更"长"
decay = 1 - j/crackLen 线性衰减 控制噼啪声的"脆"度
0.002 大爆裂概率 更多的"砰"声

五、森林:调频(FM)鸟鸣合成

5.1 为什么用 FM 合成?

真正的鸟鸣不是纯音——鸟类的鸣管(syrinx)通过肌肉调节产生频率微变。在合成中,这通过调频(Frequency Modulation)实现:

carrier(t) = sin(2π × fc × t)                    // 载波(基频)
modulator(t) = 300 × sin(2π × 0.01 × t)          // 调制信号
bird(t) = sin(2π × (fc + modulator(t)) × t)      // FM 结果

5.2 实现

List<int> _generateForest(String path, int sr, int durSec) {
  final n = sr * durSec;
  final samples = <int>[];
  double lpf = 0;

  for (var i = 0; i < n; i++) {
    final raw = _gauss();
    lpf = 0.3 * raw + 0.7 * lpf;       // 中频环境噪声
    final ambience = lpf * 0.25;

    if (_rand.nextDouble() < 0.015) {   // 1.5% 鸟鸣概率
      final birdFreq = 1200 + _rand.nextDouble() * 2000; // 1.2kHz - 3.2kHz
      final birdLen = 150 + _rand.nextInt(400);           // 150-550 样本

      for (var j = 0; j < birdLen && i + j < n; j++) {
        final decay = 1 - (j / birdLen);
        final freq = birdFreq + 300 * sin(2 * pi * 0.01 * j); // FM!
        final chirp = sin(2 * pi * freq * (i + j) / sr) * decay * 0.35;
        samples.add(((ambience * 0.6 + chirp) * 14000).toInt());
      }
      i += birdLen;
      continue;
    }

    samples.add((ambience * 18000).toInt());
  }
  return samples;
}

六、四种算法的频谱对比

频率 (Hz)    20    50   100   200   500  1000  2000  5000 10000
─────────────────────────────────────────────────────────────
雨声 (高通)   ░     ░    ░░   ░░░  ████  ███  ██    ░     ·
海浪 (1/f²)   ████  ██   █    ░    ░     ·    ·     ·     ·
篝火 (混合)   ██    ██   █░   █░   ░░    ░    ·     ·     ·
森林 (中频)   ░     █    ██   ███  ██    ░    ░░    ·     ·
  • 雨声在高频段能量集中 → 清脆感
  • 海浪在极低频能量集中 → 低沉感
  • 篝火在中低频有持续能量 + 高频随机尖峰 → 温暖 + 噼啪
  • 森林在中频有持续能量 + 高频调频啁啾 → 宁静 + 鸟鸣

七、总结:噪声 → 滤波 → 调制

四种自然音效,本质上都是噪声 + 滤波器 + 调制的排列组合:

音效 噪声源 滤波器 调制方式
雨声 高斯白噪声 高通 (raw - LPF) 双正弦包络 + 随机脉冲
海浪 布朗噪声 (积分) 自然低频 (1/f²) 双波相乘包络
篝火 高斯噪声 低频保留 概率事件 + 衰减包络
森林 高斯噪声 中频 (LPF) FM 啁啾 + 衰减包络

理解这三个要素,你就能合成任何自然声——雷声(低频 + 稀疏强脉冲)、溪流(中频 + 密集小脉冲)、蝉鸣(高频正弦 + AM 调制)。


上一篇程序化生成音频:用 Dart 手写 WAV 编码器
下一篇PNG 图片格式详解


作者简介:E-Brufen Dev,Flutter & 鸿蒙开发者。本文中的信号处理知识参考了 Julius O. Smith 的《Physical Audio Signal Processing》,项目地址:AtomGit - E-Brufen

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐