oscillatornode 通过 periodicwave 配合 real/imag 系数实现自定义波形合成,基于傅里叶级数描述频谱,需设 real[0]=imag[0]=0,长度相等且≥2,支持失真、金属音等效果,推荐预生成复用以优化性能。

AudioContext 的 OscillatorNode 本身不支持直接传入任意波形数组生成声音,但它可以通过 PeriodicWave 配合自定义的实部(real)和虚部(imag)系数,实现真正意义上的自定义波形合成——这是生成独特音效(如方波失真、金属质感、脉冲哨音等)的核心方法。
理解 PeriodicWave 的数学基础
PeriodicWave 并非直接接受采样点数组,而是基于傅里叶级数,用一组复数系数描述一个周期波形的频谱构成。浏览器内部会将这些系数还原为时域波形进行播放。你需要提供两组 Float32Array:real(余弦分量幅值)和 imag(正弦分量幅值),索引 0 对应直流分量(通常设为 0),索引 1 对应基频,索引 2 对应二次谐波……依此类推。
- 数组长度决定最高谐波次数(例如长度为 128,就包含 DC + 基频到 127 次谐波)
- real[0] 和 imag[0] 应设为 0(避免直流偏移导致破音或硬件保护)
- real 和 imag 长度必须相等,且至少为 2
手动生成常见自定义波形示例
以下代码生成一个“削顶正弦波”——在正弦基础上叠加奇次谐波并控制幅度衰减,产生类似过载合成器的温暖失真感:
const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); const real = new Float32Array(64); const imag = new Float32Array(64); <p>// 清零 DC 分量 real[0] = imag[0] = 0;</p><p>// 构建基频(1st harmonic):正弦波主干 real[1] = 1.0; // 余弦项主导 → 相位对齐起始点 imag[1] = 0;</p><p>// 添加 3rd、5th、7th 谐波(模拟软削波) for (let h = 3; h </p><p>const customWave = audioCtx.createPeriodicWave(real, imag); const osc = audioCtx.createOscillator(); osc.setPeriodicWave(customWave); osc.frequency.value = 440; // A4 osc.connect(audioCtx.destination); osc.start();</p>
从采样点数组转换为 PeriodicWave(实用技巧)
如果你已有理想波形的时域采样点(比如 1024 点的 wavetable),可用 FFT 快速转换。推荐使用轻量库 fft.js 或手动调用 Math.cos/sin 计算离散傅里叶变换(DFT):
- 确保采样点数量是 2 的幂(如 512、1024),便于 FFT
- 对时域数据做归一化(-1 ~ +1),避免溢出
- 执行 FFT 后,取前 N/2+1 个复数结果作为 real/imag 输入(N 是采样点数)
- 注意:Web Audio 的 PeriodicWave 要求 real[0] 和 imag[0] 为 0,所以需手动清零直流分量
注意事项与性能优化
自定义波形虽灵活,但需规避常见陷阱:
- 不要在 render loop 中频繁重建 PeriodicWave:它是昂贵操作,应在初始化时创建并复用
- 避免过高谐波数量:超过 100 项可能引入高频噪声或 aliasing,人耳可听范围约 20–20k Hz,440 Hz 基频下,100 次谐波已达 44 kHz,超出奈奎斯特频率,易失真
- 动态修改波形?用 WebAssembly 或 Worker:实时更新 real/imag 数组需重新 createPeriodicWave,无法原地修改;如需交互式波表扫描,建议预生成多组 wave 并切换
- 移动端兼容性:iOS Safari 支持良好,但需确保用户手势触发(如点击按钮后才 start() oscillator)
掌握 PeriodicWave 就掌握了波形设计的底层钥匙——它不靠拼接音频片段,而是用数学定义声音本质。只要系数合理,就能稳定输出从复古芯片音到科幻激光声的任意数字音色。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











