Morph 博客
工程

我们如何在浏览器中构建一个DAW

Soundbend 基于 WebAssembly、Web Audio API 和 25 年的 DSP 研究运行——全部在客户端。完整工程故事:音频图、工作线程、Airwindows 移植、真正的峰值限制器、插件主机。

作者:HAYDEN SIMERLY12分钟阅读更新于2026年4月

This is a technical post about how Morph's Soundbend — a full digital audio workstation including 5 native instruments, a Mixer with FX chains and aux sends, 30+ effects (including 5 Airwindows Tier-1 ports and a true-peak limiter), a MIDI effects rack, a WAM v2 plugin host, and a Yjs CRDT collaboration layer — runs entirely in a browser tab.

The practical question is: is this actually competitive with Ableton? The honest answer: for most producer workflows, yes. For deep Max for Live integrations, not yet. Read the Ableton alternative comparison for the product-level breakdown. This post is the engineering.

技术栈

1. 音频图表

Soundbend中的每个音轨都通过一个链路路由: 乐器 → 音轨FX链 → 辅助发送 → 混音器轨道 → 主总线 → 限制器 → 目标。每个箭头都是一个Web Audio AudioNode连接。

Web Audio为我们提供了AudioContextGainNodeBiquadFilterNodeConvolverNodeAnalyserNode及同类功能。这些由浏览器引擎(V8/SpiderMonkey/JSC均深度投入)优化。对于标准效果——均衡、增益自动化、基础滤波——我们使用原生节点。它们是快速且经过充分测试的。

自定义效果——Airwindows端口、我们的饱和度、我们的真正峰值限制器——我们使用AudioWorkletProcessor,它在音频线程上运行,保证实时优先级,并且是非标准DSP的正确架构位置。

class PurestDriveProcessor extends AudioWorkletProcessor {
  process(inputs, outputs) {
    const input = inputs[0];
    const output = outputs[0];
    // Airwindows PurestDrive port — ~30 LOC of saturation math
    for (let ch = 0; ch < input.length; ch++) {
      for (let i = 0; i < input[ch].length; i++) {
        const s = input[ch][i];
        // [...tape-saturation math...]
        output[ch][i] = processed;
      }
    }
    return true;
  }
}
registerProcessor('purest-drive', PurestDriveProcessor);

Worklets以public/audio-worklets/*.js形式打包,并通过audioContext.audioWorklet.addModule(path)加载。加载后,图谱的任意部分均可实例化AudioWorkletNode以使用该处理器。处理过程在主线程外运行——UI动画不会导致音频处理卡顿。

2. 5个Airwindows Tier-1端口

Chris Johnson's Airwindows提供2,500多个音频插件,采用MIT许可,以其卓越的DSP著称。我们已将其中5个移植到Soundbend的Wave 12中,用于AudioWorklets:

每个端口在 AudioWorkletProcessor 中包含约 30-80 行数学代码。端口与 Airwindows 并非逐位匹配(C++ 和 JS 数值引擎在 FMA/非正规处理上存在差异),但音质匹配。零测试确保输出残余 ≤ -80 dBFS,与基准对比,因此 DSP 变化会被捕捉。

3. 真峰值限制器

现代流媒体平台(Spotify、Apple Music、YouTube)根据ITU-R BS.1770-5强制执行真峰值响度标准化。样本峰值和交叉样本峰值是不同的;信号可以在样本级别达到-1 dBFS,但在数模转换后真峰值超过0 dBFS。非真峰值限制器即使在DAW中显示干净,也可能在DAC中削波。

我们的限幅器实现了完整的ITU标准:4倍过采样、样本间峰值检测、前瞻、软膝释放。它以AudioWorklet形式提供,位于public/audio-worklets/true-peak-limiter-processor.js。与参考实现(Oxford Limiter、iZotope Ozone Maximizer)的测量结果在LUFS集成值上相差不超过0.1 dB。

4. 离主线通信

音频图谱在音频线程上运行。用户界面在主线程上运行。参数更改(旋转旋钮、淡入淡出轨道)需要从UI → 音频无延迟地传播。

Web Audio 的 AudioParam 原生处理标准参数(GainNode gain、BiquadFilterNode frequency)。对于自定义工作流参数,我们使用 MessagePort 搭配结构化克隆处理 UI 消息,并使用 SharedArrayBuffer 支持的环形缓冲区处理高频自动化数据。

SharedArrayBuffer需要 Cross-Origin-Opener-Policy: same-origin + Cross-Origin-Embedder-Policy: require-corp 头部。这些在 public/_headers 中为 /soundbend/pulse 路由设置。Tape Delay使用96kHz的环形缓冲区以避免消息端口延迟。

5. 5 个原生乐器

Soundbend提供5种自定义乐器:

FM合成 —— 6运算器FM合成,基于DX7/Operator建模。算法可视化器 + 完整调制矩阵。UI约800行JS,语音分配器约300行工作线程。

波表合成器 — 在波表之间进行合成。频谱扭曲 + 单音 + 立体声微调。30 多个预设波表。使用 ConvolverNode 实现音频速率下的波表交叉淡入淡出。

模拟减法——2振荡器多音色合成器,配备Cytomic SVF滤波器。Cytomic是状态变量滤波器拓扑,声音效果正确;原始SVF在fc > SR/6时会有混叠问题。Cytomic端口通过零延迟反馈数学处理此问题。

DrumRack + Sampler — 基于垫的采样播放,每垫支持音高/滤波器/音量包络。采样存储在IndexedDB中以便快速调用。提供5种风格化预设套件(boombap、trap、house、dnb、afrobeat)。

SeedSynth + Geist(在旧版AudioStudioEngine中——现已合并至Soundbend,自Wave 14起)——一种基因化插件生成器(Synplant 2类)和一款Geist风格的鼓机。这些是使合并原则成为重要事件的专有知识产权——我们必须在30,000行合并中保持比特级行为一致。合并后通过了100个种子的零测试。

6. 混音器 + 辅助输出

一个真正的混音器——不仅仅是每轨推子,而是每轨的FX插入点、前置/后置推子辅助发送、主总线——这才是DAW与 sequencer 的区别所在。Soundbend 的混音器类约为 655 行代码(src/audio/mixer.js)。每轨都是一个带有插入槽、发送接入点和计量功能的 AudioNode 图。

棘手的部分是预推子和后推子辅助发送。预推子发送到混响总线,与轨道音量无关(经典用法:平行混响,即使轨道淡出也能保留)。后推子随轨道音量缩放。两者均提供。

7. MIDI + WAM 插件主机

WebMIDI 已经成熟——来自任何 USB-MIDI 键盘的控制器输入即开即用。我们的 MIDI 音轨(琶音器、调式、和弦)位于控制器和乐器之间,转换音符流。

对于插件,我们托管 Web Audio Modules v2 (WAM) — 浏览器原生插件。生态系统规模小于 VST(领先 25 年),但正在增长。加载 WAM 的方式为 getWamHost().load(wamUri),返回一个 AudioNode,可插入任意链路中。

WAMs 需要每路由的 CSP,因为它们会加载任意用户提供的 JS。我们的 _headers/soundbend 紧密地设置 CSP,使用 script-src 允许列表,仅允许已知的 WAM 注册表。

8. OfflineAudioContext用于回弹

导出通过 OfflineAudioContext 运行——根据链复杂度以快于实时(或更慢)的速度渲染到静态 AudioBuffer,然后通过我们浏览器内的编码器编码为WAV。

注意:并非所有节点在 OfflineAudioContext 中都能工作。MediaStreamAudioSourceNode 不行(显然无法离线录制实时麦克风)。某些工作线程在在线 → 离线转换时需要仔细重置状态。我们反复遇到这个问题,现在 Mixer.toJSON() + Mixer.fromJSON(offlineCtx) 是标准模式:序列化混音器状态,在离线上下文中重建并行混音器,进行渲染。

9. 通过Yjs协作

Yjs 是一个CRDT库,用于处理多人协作编辑并自动解决冲突。我们的 SoundbendSession 类通过每轨道文档结构(Y.Map用于轨道元数据,Y.Array用于模式步骤,Y.Text用于歌词/注释)封装Yjs。

两位至四位制作人可以同时编辑同一项目,实现样本级精确的轨道同步。这比文本文档协作更难,因为音频数据量大几个数量级——我们通过Yjs共享元数据和模式,而非原始音频。原始样本保留在本地(每个用户独立IndexedDB)。

10. 性能预算

在浏览器中运行完整的DAW,同时实现60fps UI + 实时音频 + SharedArrayBuffer + WASM + 多个工作线程,需要严格的预算。一些数据:

11. 空值测试框架

DSP 回归是无声的——音频以细微方式听起来不同,代码审查无法捕捉。Soundbend 随附一个 Puppeteer 驱动的空测试框架,位于tests/audio-null/

对于每个效果×预设×测试信号,测试框架通过OfflineAudioContext在无头Chromium中渲染,将结果WAV与提交的黄金标准进行null测试,如果残留峰值超过-60 dBFS则构建失败。

Wave 14F新增了100个SeedSynth种子基线和32个Geist鼓点黄金模式。当我们将Soundbend合并到AudioStudio(Wave 14B-G)时,null-test检测到了任何可能导致专有乐器无声损坏的问题。100个SeedSynth种子中有96个通过了位相同测试。4个失败是预存在的Chromium Web Audio问题(OfflineAudioContext实例之间的高Q滤波器共振漂移)——并非合并回归问题,且无法通过JS修复。

DSP回归是无声的。代码审查无法听到它们。空测试工具可以——这就是为什么它会阻止CI。

12. Why browser + client-side was the right bet

原生 DAW(Ableton、Logic、FL Studio)已有30年领先优势。在功能上超越它们是一条漫长的道路。但定价——通过做出不同的架构选择,可以立即赢得胜利。

WebAssembly 在 2022-2024 年跨越了性能阈值。Web Audio AudioWorklets 在同一时期广泛推出。SharedArrayBuffer + COOP/COEP 稳定化。Chromium、Firefox 和 Safari 均支持所需 API 且性能充足。

定价影响:如果音频处理在用户设备上运行,我们无需为此付费。我们的每位 Pro 订阅者的边际成本接近于零。因此 $4.99/mo 是可持续的永久。拉各斯的一名孩子使用2020年安卓设备,可获得一个完整的DAW,而旧金山的某人支付相同的 $4.99 即可访问。

阅读定价架构论文了解经济原理。对于DSP,这几乎就是全部故事。

立即尝试

Soundbend 可在 morph.cool/soundbend 找到。免费版无需注册即可使用。$4.99/月 Pro 版解锁母带处理链 + 真峰值限制器 + 额外预设。WebMIDI 键盘欢迎使用,但非必需。

打开 Soundbend

— Hayden和Morph代理团队
2026年4月22日