npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

audio-recorder-worklet-processor

v0.0.9

Published

基于 AudioWorklet 的浏览器端实时录音库,提供音频采集、降噪、格式转换等原子能力。

Readme

audio-recorder-worklet-processor

基于 AudioWorklet 的浏览器端实时录音库,提供音频采集、降噪、格式转换等原子能力。

不做音频数据存储,仅提供原子能力。业务层可自行缓存数据,再调用 encodePCM / encodeWAV 转换格式。 内置多种降噪算法,可根据业务需求选择。

浏览器支持

| Chrome | Firefox | Edge | Opera | Safari | |--------|---------|------|-------|--------| | 66+ | 76+ | 79+ | 53+ | 14.1+ |

音频处理流程

┌──────────┐    ┌──────────────┐    ┌────────────┐    ┌───────────────┐    ┌────────────┐
│ 麦克风   │───▶│ RNNoise?     │───▶│ Biquad?    │───▶│ MainWorklet   │───▶│ Destination│
│          │    │ (AI降噪-A)   │    │ (带通滤波) │    │ (采集+音量)   │    │ (扬声器)   │
└──────────┘    └──────────────┘    └────────────┘    └───────┬───────┘    └────────────┘
                                                           │ postMessage
                                                           ▼
                                                     ┌──────────────┐
                                                     │ 主线程回调    │
                                                     │ DeepFilter?  │── 48kHz降噪 → 重采样回目标采样率
                                                     │              │
                                                     │ onDataProcess│── { vol, buffer }
                                                     └──────────────┘

降噪方案(互斥,三选一)

| 方案 | 原理 | 延迟 | 效果 | 备注 | |------|------|------|------|------| | RNNoise | AudioWorklet 线程内 RNN 推理 | 极低 | 中 | 需引入 WASM + Worklet 文件 | | DeepFilter | 主线程 DeepFilterNet3 WASM 推理 | ~10-20ms | 高 | 需联网加载模型,内部 48kHz 自动重采样 | | Vonage | 替换麦克风流轨道 | 低 | 中 | 第三方 SDK |

另有 Biquad 带通滤波器(传统 DSP),可与上述方案叠加使用。


安装

npm install audio-recorder-worklet-processor

引入

ES Module:

import Recorder from "audio-recorder-worklet-processor";

const recorder = new Recorder();

CDN / Script 标签:

<script src="../dist/index.js"></script>
<script>
  const recorder = new Recorder();
</script>

API

init(config?)

初始化录音环境,创建 AudioContext 和各音频节点(不获取麦克风、不连接链路)。

await recorder.init(config);
interface IConfig {
  /** 实时音频回调,返回音量(0-1)和 Float32 原始数据 */
  onDataProcess?: (data: { vol: number; buffer: Float32Array }) => void;

  /** 音频采集参数 */
  processOptions?: IProcessOptions;

  /** 频谱分析参数(可用于可视化) */
  analyserOptions?: IAnalyserOptions;

  /** Biquad 带通滤波器参数 */
  noiseReductionOptions?: INoiseReductionOptions;

  /** 是否使用 Vonage AI 降噪,默认 false */
  useVonage?: boolean;

  /** RNNoise 降噪参数 */
  rnnoiseOptions?: IRnnoiseOptions;

  /** DeepFilter 降噪参数(内部 48kHz 处理后自动重采样回 sampleRate) */
  deepFilterOptions?: IDeepFilterOptions;
}

| 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | processSize | number | 4096 | 每次回调的采样数 | | numChannels | 1 \| 2 | 1 | 声道数 | | sampleBits | 16 \| 8 | 16 | 采样位深 | | sampleRate | number | 16000 | 采样率,常用 16000 / 48000 |

| 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | open | boolean | false | 是否开启 AnalyserNode | | fftSize | number | 512 | FFT 精度,范围 32-32768,越大越精确 |

| 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | open | boolean | false | 是否启用 | | frequency | number | 1300 | 中心频率 Hz(人声 300-3400Hz) | | Q | number | 1 | 品质因数,决定带宽 = frequency/Q |

| 参数 | 类型 | 说明 | |------|------|------| | useRnnoise | boolean | 是否启用 | | rnnoiseWasmPath | string | rnnoise.wasm 文件路径 | | rnnoiseSimdWasmPath | string | rnnoise_simd.wasm 文件路径(SIMD 加速) | | rnnoiseWorkletPath | string | rnnoise.worklet.js 文件路径 |

| 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | useDeepFilter | boolean | false | 是否启用 | | cdnUrl | string | https://cdn.laptrinhai.id.vn/deepfilternet3 | WASM/模型资源 CDN 地址 | | attenuationLimit | number | 50 | 降噪强度 dB,0-100,越大越强 | | postFilterBeta | number | 0.02 | 后滤波强度,0-0.05,0=禁用 |

注意: 首次 init() 需联网加载 WASM + 模型(约 2-4MB),耗时数秒,建议应用启动时预初始化。启用后自动关闭浏览器原生降噪/回声消除/自动增益。内部 48kHz 处理后自动重采样回你配置的 sampleRate


start()

开始录音,获取麦克风并建立音频链路。

await recorder.start();

stop()

停止录音。会将缓冲区中不足 processSize 的剩余数据再回调一次 onDataProcess,不丢数据。

await recorder.stop();

destroy()

销毁录音器,彻底释放所有资源。调用后需重新 init() 才能使用。

await recorder.destroy();

getAnalyserData()

获取实时频谱数据(需在 init 时开启 analyserOptions.open)。

const data: Uint8Array = recorder.getAnalyserData();

encodePCM(bytes, sampleBits?, littleEdian?)

Float32 原始数据 → PCM DataView。

| 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | bytes | Float32Array | — | onDataProcess 返回的原始数据 | | sampleBits | 16 \| 8 | init 配置 | 采样位深 | | littleEdian | boolean | 自动检测 | 字节序 |

const pcmData = recorder.encodePCM(bufferArr);

encodeWAV(buffer, sampleRate?, numChannels?, sampleBits?, littleEdian?)

PCM DataView → WAV DataView。

| 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | buffer | DataView | — | encodePCM 返回的数据 | | sampleRate | number | init 配置 | 采样率 | | numChannels | 1 \| 2 | init 配置 | 声道数 | | sampleBits | 16 \| 8 | init 配置 | 采样位深 | | littleEdian | boolean | 自动检测 | 字节序 |

const wavData = recorder.encodeWAV(pcmData);

dataViewToBase64(dataView)

DataView → base64 字符串。

const base64 = recorder.dataViewToBase64(wavData);

示例

基础录音 + 频谱

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Recorder Demo</title>
    <script src="../dist/index.js"></script>
  </head>
  <body>
    <button onclick="init()">init</button>
    <button onclick="start()">start</button>
    <button onclick="stop()">stop</button>
  </body>
  <script>
    const recorder = new Recorder();
    const bufferArr = [];

    const init = () => {
      recorder.init({
        analyserOptions: { open: true },
        onDataProcess: (data) => {
          bufferArr.push(...data.buffer);
        },
      });
    };

    const start = async () => {
      await recorder.start();
    };

    const stop = async () => {
      await recorder.stop();
      const pcmData = recorder.encodePCM(bufferArr);
      const wavData = recorder.encodeWAV(pcmData);
      const wavBlob = new Blob([wavData], { type: "audio/wav" });
      const oA = document.createElement("a");
      oA.href = URL.createObjectURL(wavBlob);
      oA.download = "recorder.wav";
      oA.click();
    };
  </script>
</html>
</html>

DeepFilter AI 降噪

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>DeepFilter Demo</title>
    <script src="../dist/index.js"></script>
  </head>
  <body>
    <button onclick="init()">init</button>
    <button onclick="start()">start</button>
    <button onclick="stop()">stop</button>
  </body>
  <script>
    const recorder = new Recorder();
    const bufferArr = [];

    const init = async () => {
      await recorder.init({
        deepFilterOptions: {
          useDeepFilter: true,
          attenuationLimit: 50,  // 降噪强度 0-100
          postFilterBeta: 0.02,  // 后滤波 0-0.05
        },
        processOptions: {
          numChannels: 1,
          sampleBits: 16,
          sampleRate: 16000,  // 可自由配置,DeepFilter 内部 48kHz 处理后自动重采样回来
        },
        onDataProcess: (data) => {
          bufferArr.push(...data.buffer);
        },
      });
      console.log("DeepFilter ready");
    };

    const start = async () => {
      await recorder.start();
    };

    const stop = async () => {
      await recorder.stop();
      const pcmData = recorder.encodePCM(bufferArr);
      const wavData = recorder.encodeWAV(pcmData);
      const wavBlob = new Blob([wavData], { type: "audio/wav" });
      const oA = document.createElement("a");
      oA.href = URL.createObjectURL(wavBlob);
      oA.download = "denoised.wav";
      oA.click();
    };
  </script>
</html>
</html>

生命周期

init()  ──▶  start()  ──▶  stop()  ──▶  start()  ──▶  ...
  │              │            │
  │              └─ onDataProcess 循环回调
  │
  └──────────────────────────▶  destroy()  ──彻底释放
  • init() 可重复调用(会关闭旧 context),但录音中不允许
  • stop() 后状态回到 ready,可再次 start()
  • destroy() 后需重新 init() 才能使用