没有音频设计师、没有素材库、没有版权预算——但你需要四种自然音效。E‑Brufen 的答案是用数学算法生成一切。本文详解从 WAV 文件格式到完整 Dart 编码器的实现过程。一、为什么手写 WAV 编码器1.1 问题的起点E‑Brufen 的白噪音功能需要四种自然音效雨声、海浪、篝火、森林。传统做法方案问题购买音效素材版权费用 授权复杂免费素材网站质量参差不齐 使用限制自己录制设备门槛 环境噪音网络流媒体需要 INTERNET 权限E‑Brufen 是离线应用1.2 E‑Brufen 的选择用 Dart 代码生成音频。优势零版权风险数学算法不涉及著作权零网络依赖完全离线零外部依赖不引入第三方音频库可参数化定制改参数 改音效风格可复现构建任何机器运行同样脚本得到同样结果二、WAV 文件格式详解2.1 整体结构WAV 是微软和 IBM 开发的 RIFF 容器格式结构极其简单偏移量 内容 大小 0x00 RIFF 4 bytes 0x04 文件总大小 - 8 4 bytes (小端) 0x08 WAVE 4 bytes 0x0C fmt 4 bytes 0x10 fmt chunk 大小 16 4 bytes 0x14 音频格式 1 (PCM) 2 bytes 0x16 声道数 1 (mono) 2 bytes 0x18 采样率 22050 4 bytes 0x1C 字节速率 4 bytes 0x20 块对齐 2 2 bytes 0x22 位深度 16 2 bytes 0x24 data 4 bytes 0x28 数据大小 4 bytes 0x2C 样本数据... N bytes (16-bit signed LE)总文件头大小44 字节固定。之后全是原始 PCM 样本数据。2.2 关键参数的选择constsampleRate22050;// 22.05 kHz — 自然音效的高频成分少constdurationSec30;// 30 秒 — 循环播放足够constchannels1;// Mono — 白噪音不需要立体声constbitDepth16;// 16-bit — 标准 CD 音质参数值理由采样率22050 Hz自然音效频谱主要在低频22kHz 完全足够音乐才需要 44.1kHz时长30 秒循环播放30 秒的循环无缝感好声道1 (Mono)减小文件体积自然音效立体声意义不大位深16-bit标准质量每个样本 2 字节文件大小计算22050 样本/秒 × 30 秒 × 2 字节/样本 1,323,000 字节 ≈ 1.3 MB 4 种音效 × 1.3 MB ≈ 5.2 MB完全可接受三、完整 Dart 实现注上图原为 WAV 文件结构示意图来自 CSDN 图床。若无法显示请参考文末“可视化辅助”章节中的文本结构图。3.1 主函数// generate_audio.dartimportdart:io;importdart:math;final_randRandom();voidmain(){finaldirDirectory(assets/audio);if(!dir.existsSync())dir.createSync(recursive:true);print(Generating rain.wav...);_generateRain(${dir.path}/rain.wav,22050,30);print(Generating ocean.wav...);_generateOcean(${dir.path}/ocean.wav,22050,30);print(Generating fire.wav...);_generateFire(${dir.path}/fire.wav,22050,30);print(Generating forest.wav...);_generateForest(${dir.path}/forest.wav,22050,30);print(Done!);}3.2 WAV 写入函数void_writeWav(Stringpath,int sampleRate,Listintsamples){finalbytesint[];// 辅助函数写入多字节整数小端字节序voidw32(int v){bytes.add(v0xFF);bytes.add((v8)0xFF);bytes.add((v16)0xFF);bytes.add((v24)0xFF);}voidw16(int v){bytes.add(v0xFF);bytes.add((v8)0xFF);}finaldataSizesamples.length*2;// 16-bit 2 bytes/samplefinalfileSize36dataSize;// 36 header after RIFF size field// ── RIFF Header ──bytes.addAll(RIFF.codeUnits);w32(fileSize);bytes.addAll(WAVE.codeUnits);// ── fmt chunk ──bytes.addAll(fmt .codeUnits);w32(16);// fmt chunk size (PCM 16)w16(1);// audio format (1 PCM)w16(1);// channels (1 mono)w32(sampleRate);// sample rate (22050)w32(sampleRate*2);// byte rate (sampleRate * channels * bitsPerSample/8)w16(2);// block align (channels * bitsPerSample/8 2)w16(16);// bits per sample (16)// ── data chunk ──bytes.addAll(data.codeUnits);w32(dataSize);// ── 样本数据16-bit signed integer, little-endian ──for(finalsinsamples){finalvs.clamp(-32768,32767);// 16-bit 有符号范围if(v0){w16(0x10000v);// 补码转换-1 → 0xFFFF}else{w16(v);}}File(path).writeAsBytesSync(bytes);}★ Insight ─────────────────────────────────────0x10000 v是有符号整数转无符号小端的技巧。Dart 的int是任意精度而 WAV 格式要求 16-bit 补码。当v -1时0x10000 (-1) 0xFFFF——恰好是 -1 的 16-bit 补码表示。这个一行代码的技巧避免了手动处理补码的复杂度。─────────────────────────────────────────────────四、WAV vs 其他格式格式复杂度压缩文件大小 (30s)是否需要解码库WAV极低无~1.3 MB❌ 不需要MP3极高有损~0.3 MB✅ 需要OGG高有损~0.2 MB✅ 需要FLAC中无损~0.7 MB✅ 需要WAV 的优势编码器只需 60 行代码任何播放器都能直接打开。对于 30 秒的短音频1.3MB 的文件大小完全可以接受。五、验证生成的 WAV# 检查文件头xxd rain.wav|head-3# 00000000: 5249 4646 ... 5741 5645 RIFF....WAVE# 00000010: 666d 7420 1000 0000 0100 fmt ........# 00000020: 0100 2256 0000 44ac 0000 ..V..D...# 用任意播放器测试# Windows: start rain.wav# macOS: open rain.wav六、可视化辅助补充虽然原文中的图片链接可能来自外部图床但为了便于您理解 WAV 的内存布局这里提供两种可替代的可视化方案您可以直接嵌入文章或文档中使用。6.1 Mermaid 代码渲染后为流程图RIFF Header0x00-0x03Chunk Size0x04-0x07WAVE ID0x08-0x0Bfmt 块0x0C-0x23data 块头0x24-0x2BPCM 样本数据0x2C ~ EOF6.2 纯文本 ASCII 结构表偏移量(Hex) 字段名称 大小(字节) 值/说明 0x00 RIFF 4 固定标识 0x04 文件大小-8 4 小端整数 0x08 WAVE 4 格式标识 ----------------------------------------------- 0x0C fmt 4 块标识(带空格) 0x10 fmt块长度 4 0x10 (16) 0x14 音频格式 2 0x01 (PCM) 0x16 声道数 2 0x01 (Mono) 0x18 采样率 4 0x5622 (22050Hz) 0x1C 字节速率 4 0xAC44 (44100) 0x20 块对齐 2 0x02 0x22 位深度 2 0x10 (16bit) ----------------------------------------------- 0x24 data 4 数据块标识 0x28 数据大小 4 N字节 0x2C 样本数据 N 16-bit 小端有符号这些辅助内容与原文图片相辅相成保证读者即便无法加载外部图片也能完全掌握 WAV 格式。七、后续与扩展本文聚焦于WAV 编码器的纯 Dart 实现而四种音效的具体生成算法粉红噪声、分形噪声、幅度调制等将在下一篇中详细展开。届时您将看到白噪声 → 粉红噪声的滤波器设计海浪的低频振荡包络篝火爆裂事件的泊松分布模拟森林风噪与鸟鸣的 FM 合成所有算法均不依赖任何外部资源仅靠dart:math完成。下一篇高斯噪声模拟自然音效的数学原理作者简介E‑Brufen DevFlutter 鸿蒙开发者。WAV 编码器的实现参考了 WAV PCM soundfile format 规范,项目地址AtomGit - E‑Brufen。