欢迎光临
我们一直在努力

Web Audio API 振荡器与滤波包络实战:在浏览器中合成 808 经典底鼓

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}

Web Audio API 振荡器与滤波包络实战:在浏览器中合成 808 经典底鼓

封面信息图

在现代电子音乐、嘻哈(Trap)与工业摇滚中,Roland TR-808 鼓机的底鼓(808 Kick)是奠定整首乐曲低频灵魂(Sub-bass Punch)的绝对基石。

很多前端开发者在做 Web 音频交互或网页游戏打击乐时,往往习惯直接通过 new Audio('kick.mp3').play() 播放一段预录好的音频切片。这种方式存在三大致命缺陷:

  • 音高与衰减死板不可调:无法动态改变底鼓的音高(Tune)、起音冲击力(Attack Punch)或长尾衰减时间(Decay);
  • 高频触发存在网络与解码延迟:在 140 BPM 的快速连击下,频繁调用 play() 会产生严重的音频卡顿与爆音;
  • 资源体积冗余:引入几十个音频采样切片会增加静态包体积。
  • 利用浏览器原生内置的 Web Audio API 节点图(Audio Graph),通过 正弦波振荡器(OscillatorNode)、指数音高瞬态包络(Pitch Envelope) 与 增益衰减器(GainNode),我们能够用不到 50 行纯 TypeScript 代码,在浏览器内存中实时物理合成出震撼、紧实、毫秒级零延迟的经典 808 模拟底鼓。

    808 底鼓的声学物理合成模型

    一个充满力量感与胸腔共鸣的 808 底鼓,在物理声学上由两部分构成:

  • 瞬态起音冲击(Click / Attack Punch,0 ~ 30ms):音高在 30 毫秒内从高频(约 150~180 Hz)瞬间急剧向下俯冲衰减至基频(约 50 Hz),模拟鼓槌击打鼓皮的瞬间张力;
  • 正弦低频长尾共鸣(Body / Sub-bass Decay,30 ~ 800ms):纯净的正弦波维持在 45~55 Hz 低频基音,振幅沿着指数曲线缓慢平滑衰减。
  • 【808 底鼓合成 AudioNode 拓扑图】
    [正弦波振荡器 (OscillatorNode: sine)]
    – frequency 参数由指数音高包络控制 (160Hz ──> 50Hz)
    │
    ▼
    [动态增益包络器 (GainNode: volume envelope)]
    – gain 参数由指数音量包络控制 (1.0 ──> 0.001)
    │
    ▼
    [低通滤波器 (BiquadFilterNode: lowpass 250Hz)]
    – 滤除高频杂音,强化浑厚 Sub-bass
    │
    ▼
    [音频输出上下文 (AudioContext.destination)]

    核心实现:生产级 Web 808 纯代码合成引擎

    export interface Kick808Params {
    baseFrequency?: number; // 基频 (默认 50 Hz)
    punchFrequency?: number; // 起音最高冲击频率 (默认 160 Hz)
    punchTime?: number; // 俯冲起音时间 (默认 0.035 秒)
    decayTime?: number; // 整体衰减尾音长度 (默认 0.6 秒)
    gain?: number; // 主音量 (0.0 ~ 1.0)
    }

    export class WebKick808Synthesizer {
    private ctx: AudioContext;
    private masterGain: GainNode;

    constructor() {
    // 兼容各浏览器前缀
    const AudioCtx = window.AudioContext || (window as any).webkitAudioContext;
    this.ctx = new AudioCtx();
    this.masterGain = this.ctx.createGain();
    this.masterGain.connect(this.ctx.destination);
    }

    public trigger(params: Kick808Params = {}) {
    const {
    baseFrequency = 50,
    punchFrequency = 160,
    punchTime = 0.035,
    decayTime = 0.6,
    gain = 0.9,
    } = params;

    // 解决浏览器自动播放安全策略 (用户首个交互后恢复上下文)
    if (this.ctx.state === 'suspended') {
    this.ctx.resume();
    }

    const now = this.ctx.currentTime;

    // 1. 创建正弦波振荡器 (808 的核心声源)
    const osc = this.ctx.createOscillator();
    osc.type = 'sine';

    // 2. 创建独立的音量包络 Gain 节点
    const gainNode = this.ctx.createGain();

    // 3. 创建低通滤波器,切掉多余高频,让低频更结实
    const filter = this.ctx.createBiquadFilter();
    filter.type = 'lowpass';
    filter.frequency.setValueAtTime(300, now);

    // ==========================================
    // 核心秘诀:音高包络(Pitch Envelope)
    // ==========================================
    osc.frequency.setValueAtTime(punchFrequency, now);
    // 在 punchTime 时间内,音高以指数曲线迅速俯冲至基频 baseFrequency
    osc.frequency.exponentialRampToValueAtTime(baseFrequency, now + punchTime);

    // ==========================================
    // 核心秘诀:音量包络(Amplitude Envelope)
    // ==========================================
    gainNode.gain.setValueAtTime(gain, now);
    // 在 decayTime 内,振幅沿指数曲线平滑衰减至接近 0 (防爆音)
    gainNode.gain.exponentialRampToValueAtTime(0.001, now + decayTime);

    // 4. 连接节点图
    osc.connect(gainNode);
    gainNode.connect(filter);
    filter.connect(this.masterGain);

    // 5. 调度发声与自动销毁释放内存
    osc.start(now);
    osc.stop(now + decayTime + 0.05);

    // 发声结束后断开连接,避免内存泄漏
    setTimeout(() => {
    osc.disconnect();
    gainNode.disconnect();
    filter.disconnect();
    }, (decayTime + 0.1) * 1000);
    }
    }

    前端 React 动态打击垫组件

    import React, { useRef, useState } from 'react';
    import { WebKick808Synthesizer } from './synth808';

    export const DrumPad808: React.FC = () => {
    const synthRef = useRef<WebKick808Synthesizer | null>(null);
    const [decay, setDecay] = useState<number>(0.6);
    const [pitch, setPitch] = useState<number>(50);

    const getSynth = () => {
    if (!synthRef.current) {
    synthRef.current = new WebKick808Synthesizer();
    }
    return synthRef.current;
    };

    const handleHit = () => {
    getSynth().trigger({
    baseFrequency: pitch,
    decayTime: decay,
    punchFrequency: pitch * 3.2,
    });
    };

    return (
    <div className="p-8 bg-slate-950 text-white rounded-3xl border border-slate-800 shadow-2xl flex flex-col items-center max-w-sm">
    <h2 className="text-xl font-black text-cyan-400 tracking-wider">808 SUB-BASS KICK</h2>
    <p className="text-xs text-slate-400 mt-1">Web Audio API 纯代码实时物理合成</p>

    {/* 实体动态打击垫按钮 */}
    <button
    onClick={handleHit}
    className="mt-8 w-40 h-40 rounded-2xl bg-gradient-to-br from-cyan-500 to-blue-600 font-black text-2xl shadow-[0_0_40px_rgba(6,182,212,0.4)] active:scale-95 active:shadow-[0_0_15px_rgba(6,182,212,0.6)] transition-all flex items-center justify-center border-2 border-cyan-300/30"
    >
    💥 PUNCH
    </button>

    {/* 参数调音面板 */}
    <div className="mt-8 w-full space-y-4 text-xs font-mono">
    <div>
    <div className="flex justify-between text-slate-300">
    <span>基调音高 (Pitch):</span>
    <span className="text-cyan-400 font-bold">{pitch} Hz</span>
    </div>
    <input
    type="range"
    min="35"
    max="80"
    value={pitch}
    onChange={(e) => setPitch(Number(e.target.value))}
    className="w-full mt-1 accent-cyan-400"
    />
    </div>

    <div>
    <div className="flex justify-between text-slate-300">
    <span>尾音衰减 (Decay):</span>
    <span className="text-cyan-400 font-bold">{decay}s</span>
    </div>
    <input
    type="range"
    min="0.2"
    max="1.5"
    step="0.05"
    value={decay}
    onChange={(e) => setDecay(Number(e.target.value))}
    className="w-full mt-1 accent-cyan-400"
    />
    </div>
    </div>
    </div>
    );
    };

    技术实测优势

  • 绝对零音频文件下载(0KB 静态资产):纯数学参数实时计算发声,彻底省去下载庞大采样包的带宽开销。
  • 纳秒级瞬时触发与零连击卡顿:依托 Web Audio 底层 C++ 音频线程,即使在 180 BPM 的高频连击下,也能保持分秒不差的精准对齐。
  • 支持全参数动态调制:开发者可以随心所欲地通过代码将滑块、鼠标移动速度或游戏爆炸强度,与音高包络和衰减时间实时联动,创造出无穷无尽的动态声音质感。
  • 赞(0)
    未经允许不得转载:171主机测评 » Web Audio API 振荡器与滤波包络实战:在浏览器中合成 808 经典底鼓
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址