FRCRN 开源模型 WebAssembly 浏览器端轻量化部署
在线会议背景噪声干扰严重,传统降噪软件往往需要安装庞大客户端。本文将介绍将阿里巴巴达摩院开源的 FRCRN 语音降噪模型,通过 WebAssembly 技术部署到浏览器端的方案,无需安装软件即可实现实时语音降噪。
将阿里巴巴达摩院开源的 FRCRN 语音降噪模型通过 WebAssembly 技术部署到浏览器端的完整流程。内容包括环境搭建、PyTorch 转 ONNX 格式转换、ONNX Runtime Web 集成、音频处理模块实现及性能优化策略。实测表明该方案无需安装客户端,数据本地处理保障隐私,在主流设备上可实现低延迟实时降噪,显著提升嘈杂环境下的语音清晰度与识别率。
在线会议背景噪声干扰严重,传统降噪软件往往需要安装庞大客户端。本文将介绍将阿里巴巴达摩院开源的 FRCRN 语音降噪模型,通过 WebAssembly 技术部署到浏览器端的方案,无需安装软件即可实现实时语音降噪。
FRCRN(Frequency-Recurrent Convolutional Recurrent Network)在频率域处理音频信号,结合卷积网络捕捉局部特征和循环网络关注长期依赖关系。该模型在单通道降噪任务上表现优异,能有效消除持续稳态噪声、突发性噪声及人声干扰,同时保持高保真度。
WebAssembly(Wasm)是一种能在现代浏览器中运行的二进制指令格式,性能接近原生代码且具备安全沙箱特性。在浏览器运行 AI 模型的优势包括:
基础要求:
安装构建工具:
# 安装必要的 npm 包
npm install -g onnxruntime-web esbuild
# 创建项目目录
mkdir frcrn-wasm-demo
cd frcrn-wasm-demo
原始模型为 PyTorch 格式,需转换为 ONNX 格式以适配 Wasm。
步骤 1:下载原始模型
import torch
from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks
ans_pipeline = pipeline(
task=Tasks.acoustic_noise_suppression,
model='damo/speech_frcrn_ans_cirm_16k'
)
print("模型下载完成!")
步骤 2:转换为 ONNX 格式
import torch
import onnx
from modelscope.models import Model
model_dir = '~/.cache/modelscope/hub/damo/speech_frcrn_ans_cirm_16k'
model = Model.from_pretrained(model_dir)
model.eval()
dummy_input = torch.randn(1, 257, 100, 2)
torch.onnx.export(
model, dummy_input, "frcrn_model.onnx",
input_names=["input"], output_names=["output"],
dynamic_axes={'input': {2: 'time'}, 'output': {2: 'time'}},
opset_version=13
)
print("ONNX 模型导出完成!")
步骤 3:优化 ONNX 模型 使用 ONNX Runtime 工具进行优化,确保算子兼容性。
frcrn-wasm-demo/
├── index.html
├── style.css
├── app.js
├── wasm/
│ ├── frcrn.onnx
│ ├── ort-wasm.wasm
│ └── ort-wasm.js
├── audio-processor.js
└── package.json
负责将麦克风采集的 PCM 数据转换为模型所需格式。
// audio-processor.js
class AudioProcessor {
constructor() {
this.audioContext = null;
this.processor = null;
this.model = null;
this.isProcessing = false;
this.SAMPLE_RATE = 16000;
this.FRAME_SIZE = 512;
this.HOP_SIZE = 256;
}
async init() {
try {
this.audioContext = new (window.AudioContext || window.webkitAudioContext)({ sampleRate: this.SAMPLE_RATE });
await this.audioContext.resume();
return true;
} catch (error) {
console.error('初始化音频上下文失败:', error);
return false;
}
}
async startProcessing() {
if (this.isProcessing) return;
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: {
sampleRate: this.SAMPLE_RATE,
channelCount: 1,
echoCancellation: false,
noiseSuppression: false,
autoGainControl: false
}
});
const source = this.audioContext.createMediaStreamSource(stream);
this.processor = this.audioContext.createScriptProcessor(this.FRAME_SIZE, 1, 1);
source.connect(this.processor);
this.processor.connect(this.audioContext.destination);
this.processor.onaudioprocess = (event) => {
if (!this.isProcessing || !this.model) return;
const inputData = event.inputBuffer.getChannelData(0);
this.processAudioFrame(inputData);
};
this.isProcessing = true;
} catch (error) {
console.error('启动音频处理失败:', error);
}
}
processAudioFrame(audioData) {
const stftData = this.computeSTFT(audioData);
const output = await this.model.run(stftData);
const processedAudio = this.inverseSTFT(output);
return processedAudio;
}
computeSTFT(audioData) { /* 实现 STFT */ }
inverseSTFT(spectrum) { /* 实现逆 STFT */ }
}
// app.js
class FRCRNApp {
constructor() {
this.audioProcessor = new AudioProcessor();
this.session = null;
this.isModelLoaded = false;
}
async loadModel() {
const loadingElement = document.getElementById('loading');
loadingElement.textContent = '正在加载模型...';
try {
const ort = await import('https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js');
this.session = await ort.InferenceSession.create('./wasm/frcrn.onnx', {
executionProviders: ['wasm'],
graphOptimizationLevel: 'all'
});
this.isModelLoaded = true;
loadingElement.textContent = '模型加载完成!';
} catch (error) {
console.error('加载模型失败:', error);
}
}
async processWithModel(inputTensor) {
if (!this.session || !this.isModelLoaded) throw new Error('模型未加载');
const feeds = { input: new ort.Tensor('float32', inputTensor, [1, 257, 100, 2]) };
const results = await this.session.run(feeds);
return results.output.data;
}
}
内存管理优化:重用内存缓冲区,避免频繁分配。
class AudioBufferPool {
constructor() { this.buffers = new Map(); }
getBuffer(size) {
if (!this.buffers.has(size)) this.buffers.set(size, new Float32Array(size));
return this.buffers.get(size);
}
}
计算优化:使用 Web Workers 并行处理 STFT。
延迟优化:根据设备性能动态调整帧大小和重叠率配置。
本文完成了 FRCRN 模型在浏览器端的部署实践,涵盖模型转换、Wasm 集成、音频处理及性能优化。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online