你把一段一小时的立体声 WAV 拖进网页工具里,文件大小 635 MB。进度条刚走到一半,页面开始变灰,鼠标点哪儿都没反应,几秒后浏览器弹出「此页面无响应」,标签页自己重启了。任务管理器里这个标签的内存峰值停在 4 GB 上下。电脑没坏,网速也够快------问题在于,浏览器不是一个可以随便摊开几百兆素材的工作台。
底层原理:文件进浏览器要先被「摊开」
磁盘上的音频文件是压缩或打包过的字节流。要播放它、画波形、做裁剪,都必须先解码成脉冲编码调制(PCM)采样点。这一步的本质,是把时间轴上的每一个采样点都变成内存里实实在在的数值。
关键在于表示方式。多数浏览器的音频处理链路把采样统一成 32 位浮点(float32)来算------即使原始文件是 16 位整数,解码后每个采样也要占 4 个字节。于是同一段音频,在磁盘上和内存里的体积完全不是一回事:
• 44.1kHz 立体声 16 位:每秒 44100 × 2 声道 × 2 字节 ≈ 176 KB,约 10.6 MB/分钟
• 同样的素材解码成 float32:每秒 44100 × 2 × 4 ≈ 353 KB,约 21.2 MB/分钟

换句话说,一条播放 10 分钟才 10 MB 的压缩流,摊开成浮点采样后就是 210 MB 上下。如果工具还要保留原始数据做前后对比、再算一遍频谱、最后留一份处理结果,同一段音频在内存里可能出现三四份副本。
上传的如果本来就是 16 位 WAV,文件里没有压缩损耗,看起来「不用解码」------但浮点表示那一步照样要占双倍空间。真正膨胀最厉害的是 MP3、AAC 这类压缩格式:128kbps 码率的 MP3 解码之后,内存体积能达到磁盘体积的十几倍。

三道墙分别卡在哪一环
第一道墙:解码是「全有或全无」
Web Audio 里最常用的解码入口是 `decodeAudioData()`。它的签名本身就说明了问题:输入是完整的文件字节,输出是一整个解码完成的缓冲区(AudioBuffer),中间没有回调、没有分块。
const buf = await ctx.decodeAudioData(arrayBuffer);
这意味着哪怕你只想处理文件最后五分钟,也得先把整段解码出来。磁盘上 635 MB 的文件,这个函数会在内存里留下一份完整的、翻倍后的 PCM。

第二道墙:内存上限是「每个标签页」的
浏览器的可用内存不是一个全局的大池子,而是按标签页、按进程划分的。单个渲染进程能用的地址空间有上限,Chrome/Edge 在 64 位系统上大致是 2~4 GB 级别,Safari 更低,32 位内核甚至只有 512 MB 左右。越界之后不是变慢,而是直接抛出内存不足、进程被系统回收。
再叠加一层:如果处理逻辑跑在 WebAssembly 里,它的线性内存必须是一段连续的地址空间,增长时只能求操作系统给一块更大的连续区域。浏览器跑久了,地址空间里满是碎片,明明总量还剩 1 GB,也未必能凑出一块连续的 512 MB。
第三道墙:缓冲和副本会成倍叠加
解码只是第一份。裁剪工具要画波形,就得再算一份降采样的包络;要看频谱分布做一次短时傅里叶变换(STFT),又要多一份与采样点等长的复数结果;要做响度归一化,得先扫一遍整段的电平均值;要在处理前后对比试听,又得留一份原始 PCM。这些中间结果如果都留在内存里,占用很快从 1.2 GB 逼近 2.5 GB。
这也是为什么同样的文件,在一台 16 GB 的机器上照样会崩------不是机器内存不够,而是浏览器愿意分给这一个标签页的额度不够。

把整段拆成装得下的几块
理解了这三道墙,处理长素材的思路就变了:不是去找一个「更强的浏览器」,而是不让任何一次操作需要整段素材同时待在内存里。实际操作可以这样走:
第一步,先判断手上属于哪种情况------压缩格式、时长超过 30 分钟的,先在本地切成几段;如果只想要中间某一段,直接按时间段截取,切出来的小文件再逐个进网页处理。
第二步,把「必须整段才能算」的操作和「只看一段」的操作分开。响度统计、整体归一化一定要扫全曲,放到本地软件做;裁剪、单段试听、逐段频段微调这些只看局部的操作,交给网页端。
第三步,每处理完一段就导出并关掉,别让多个大文件同时挂在同一个标签页里。

收尾
工具能做的是把「整段」拆成「装得下的几块」,不是把浏览器的内存上限抬高一格。先搞清楚手上的文件解码后有多大、哪一步在悄悄复制内存,比反复换浏览器、换电脑有用得多。素材规模只会一直涨,而内存账必须先算。